PL/SQLでテーブルを同期する
他システムなど外部からデータを取り込み加工して本テーブルに取り込みたい場合というのは非常に多い。非常に多いにも関わらず、SQLでこのような処理を書くのは意外と難しい。いろいろと試行錯誤した結果、FULL JOINを使う方法に落ち着いた。
FOR cur_rec IN ( SELECT S.キー1, S.キー2, S.値1, S.値2, S.値3, D.UPDATE_ROWID, (CASE WHEN D.キー1 IS NULL THEN 'I' -- 登録 WHEN S.キー1 IS NULL THEN 'D' -- 削除 WHEN S.値1 || 'X' <> D.値1 || 'X' OR S.値2 || 'X' <> D.値2 || 'X' OR S.値3 || 'X' <> D.値3 || 'X' THEN 'U' -- 更新 END) AS UPDATE_STATE FROM ( -- 同期したいデータセットを取得するSQLを書く SELECT DMT1.キー1, DMT2.キー2, DMT1.値1, DMT1.値2, DMT2.値3 FROM 同期元テーブル1 DMT1 INNER JOIN 同期元テーブル2 DMT2 ON ... WHERE ... ) S FULL JOIN ( -- 同期するデータセットを取得するSQLを書く SELECT DST.ROWID AS UPDATE_ROWID, DST.キー1, DST.キー2, DST.値1, DST.値2, DST.値3 FROM 同期先テーブル DST ) D ON D.キー1 = S.キー1 AND D.キー2 = S.キー2 ) LOOP IF cur_rec.UPDATE_STATE = 'I' THEN INSERT INTO 同期先テーブル ( ... ) VALUE ( ... ); ELSIF cur_rec.UPDATE_STATE = 'U' THEN UPDATE 同期先テーブル SET ... WHERE ROWID = cur_rec.UPDATE_ROWID; ELSIF cur_rec.UPDATE_STATE = 'D' THEN DELETE FROM 同期先テーブル WHERE ROWID = cur_rec.UPDATE_ROWID; END IF; END LOOP;
この手の処理では、SELECT INSERT や MERGE 句が使われることが多いが、ログ出力や存在チェックなどを考えるとこちらの書き方の方が柔軟だと思う。