web-dev-qa-db-ja.com

単一のUPDATE SQLステートメントで複数の更新を実行することは可能ですか?

テーブルtblに列idtitleがあるとします。タイトル列のすべての値を変更する必要があります。

  1. 「a-1」から「a1」まで、
  2. 「a.1」から「a1」へ、
  3. 「b-1」から「b1」まで、
  4. 「b.1」から「b1」へ。

現在、2つのUPDATEステートメントを実行しています。

UPDATE tbl SET title='a1' WHERE title IN ('a-1', 'a.1')
UPDATE tbl SET title='b1' WHERE title IN ('b-1', 'b.1')

テーブルが小さく、単一のステートメントが1秒未満で完了し、実行するステートメントが数個あれば、これはまったく問題ではありません。

あなたはおそらくそれを受け入れました-対処する巨大なテーブルがあります(1つのステートメントは約90秒で完了します)。

では、更新をマージして、テーブルを1回だけスキャンすることは可能ですか?あるいは、このような状況で対処するためのより良い方法があるでしょう。

編集:私が作業している実際のデータと実行しなければならないデータへの変更は実際にはそれほど単純ではないことに注意してください-文字列はより長く、パターンに従っていない(それはユーザーデータなので、仮定はありません)作ることができます-それは何でも可能です)。

28
Paulius

新しい値のそれぞれに何百ものマッピングが存在する可能性があるより一般的なケースでは、古い値と新しい値の個別のテーブルを作成し、それをUPDATEステートメントで使用します。 SQLの1つの方言で:

CREATE TEMP TABLE mapper (old_val CHAR(5) NOT NULL, new_val CHAR(5) NOT NULL);
...multiple inserts into mapper...
INSERT INTO mapper(old_val, new_val) VALUES('a.1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('a-1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('b.1', 'b1');
INSERT INTO mapper(old_val, new_val) VALUES('b-1', 'b1');
...etcetera...

UPDATE tbl
   SET title = (SELECT new_val FROM mapper WHERE old_val = tbl.title)
   WHERE title IN (SELECT old_val FROM mapper);

どちらの選択ステートメントも重要です。 1つ目は、古い値に対応するマッピングテーブルから新しい値を引き出す相関サブクエリです(必ずしも高速ではありませんが、マッパーテーブルに数千の行がある場合、ほとんどの代替よりも高速です)。 2番目は、マッピングテーブルに値を持つ行のみが変更されることを保証します。それ以外の場合、これは非常に重要です。タイトルは、マッピングエントリのない行のnullに設定されます(これらは、開始する前に問題がなかったレコードです)。

いくつかの代替案では、CASE操作は問題ありません。ただし、実行するマッピングが数百、数千、数百万ある場合は、DBMSのSQLステートメントの長さの制限を超える可能性があります。

22

1つのステートメントと複数のcaseステートメントを使用できます

update tbl
  set title = 
    case
      when title in ('a-1', 'a.1') then 'a1'
      when title in ('b-1', 'b.1') then 'b1'
      else title
    end

もちろん、これによりすべてのレコードに書き込みが発生し、インデックスがあると問題になる可能性があるため、変更する行のみをフィルターで除外できます。

update tbl
  set title = 
    case
      when title in ('a-1', 'a.1') then 'a1'
      when title in ('b-1', 'b.1') then 'b1'
      else title
    end
where
  title in ('a.1', 'b.1', 'a-1', 'b-1')

これにより、テーブルへの書き込み数が削減されます。

24
casperOne

ジョナサンの答えに基づいて作業しています。

UPDATE tbl
   SET title = new_val
FROM mapper
WHERE title IN (SELECT old_val FROM mapper)
     AND mapper.old_val = tbl.title;

彼の最初のバージョンでは、マッパーテーブルへの多数の読み取りが必要でした。

9
mrdenny

変換が例のように単純であれば、少し文字列を操作して更新を実行できます。

UPDATE tbl 
SET title = left(title, 1) + right(title, 1) 
WHERE title IN ('a-1', 'a.1', 'b-1', 'b.1')

そのようなものはあなたのために働きますか?

3
Matt Hamilton

または

   Update Table set 
     title = Replace(Replace(title, '.', ''), '-', '')
   Where title Like '[ab][.-]1'
0
Charles Bretana