読者です 読者をやめる 読者になる 読者になる

ゆるふわエンジニアのブログ

やったこと、調べたこと等をつらつらと書いていくかもしれません。

MERGE文を使用して高速一括更新(Oracle)

※注意 速度の話をしていますが、体感速度で話をしており、実速度は計っていません。
Oracle9i以降対応のお話です。


お仕事でSQL(PL/SQL)のチューニングを行った際のお話です。
備忘録的な感じでメモ。

お仕事で、下記の様なSQL文とPL/SQL文で書かれた
速度がすごく遅い処理に出会いました。

SQL

UPDATE TBL1 A
SET A.XXX = (
             SELECT
               subA.ZZZ AS ZZZ
             FROM
               TBL2 subA
             WHERE
               subA.ID = A.ID
            )


PL/SQL

DECLARE
  CURSOR C IS
    SELECT
       B.ID  AS ID
      ,B.ZZZ AS ZZZ
    FROM
      TBL2 B
BEGIN
  FOR R IN C LOOP
    UPDATE TBL1 A
    SET A.XXX = R.ZZZ
    WHERE
      A.ID = R.ID;
  END LOOP;
  COMMIT;
END;

前者は副問合せを使用しているので
「きっとSELECT文の箇所が複数回呼ばれているから遅そうだな」と、
後者は「TBL2のデータ分だけループをグルグル回しているので遅そうだな」
ということが感じとれますね。
「後者はバルク処理を使えば多少マシになるんじゃね?」とかは要らないです。

これらの一括更新処理をはやく行うためには、MERGE文を使うと良いです。
どういう風に書き換えるのかは、下記の通り。

MERGE INTO TBL1 A
USING (
       SELECT
          subA.ID  AS ID
         ,subA.ZZZ AS ZZZ
       FROM
         TBL2 subA
      ) B
  ON (A.ID = B.ID)
WHEN MATCHED THEN
  UPDATE SET
    A.XXX = B.ZZZ

このソースコードを読んで、
「アレ?WHEN NOT MATCHED THENまで書いてないけど大丈夫?」
と思われたかもしれませんが、問題ありません。
ちゃんと動きますので、ご安心を。


最後に。
SELECT INSERTを最初に行うなら、後で一括更新を行わず、
その時に表を結合して一気にINSERTしてしまったほうが良い気がしますね。
(チューニングを行った処理は、SELECT INSERT後一括更新を掛けていた。)