2021.11.26
SQLチューニング(第19回)「MERGE構文理解と効率的なSQLの作成」
今回から「MERGE構文理解と効率的なSQLの作成」と題しまして、MARGE構文についてのお話になります。それでは、早速始めましょう。
5.「MERGE構文理解と効率的なSQLの作成」
MERGE構文は、OracleでUPDATE&INSERT&DELETEのMULTIPLE OPERATIONをサポートするためのDML構文で、Source Tableから抽出したデータをTarget Tableにトランザクション(UPDATE、DELETE、INSERT)を処理する形で動作するが、実務では一般的に以下と 同じ場合によく使われます。
・ UPSERT (UPDATEまたはINSERT)
・ Only UPDATE (UPDATABLE JOIN VIEWの置き換え(Oracle 10g以後))
UPSERT構文はUPDATEとINSERTを同時に処理するプログラム ロジックを処理可能になるように実装したもので、UPDATEやINSERTを実行する対象データを抽出し、トランザクション処理するデータなのかチェックして、UPDATEまたはINSERTを処理できる効率的な構文です。
MERGE構文の基本使用目的はUPSERT構文のためといっても過言ではない。 その上Oracle 10g以降からはMERGE構文の必須トランザクション実行単位がUPSERT構文でなくOnly UPDATE、Only INSERT、UPSERTなどより多様な実行単位をサポートします。
したがってOracle 10g以前のバージョンで大量データをUPDATE時SET節にサブクエリが存在してUPDATE件数ぐらい反復実行して性能問題が発生する場合、性能改善方法でUPDATABLE JOIN VIEWを使ったが、10gからはUPDATABLE JOIN VIEWよりMERGE構文をたくさん使います。
その理由はMERGE構文は並列処理が可能なだけでなくUPDATABLE JOIN VIEWより性能的な側面で効率的で、構文作成時制約事項が少なく簡単に作成できる長所を有しているためです。
理解を助けるために例題を通じて調べてみることにしましょう。
[例題SQL]
UPDATE emp a
SET ename = (SELECT dname FROM dept b WHERE a.deptno = b.deptno)
WHERE a.empno> 0 ;
[例題SQL]のようにSET節にサブクエリを使った場合、Where節の条件を実行後抽出されるデータ件数が多いならば、反復的なサブクエリの実行で性能問題が発生するでしょう。
このような場合、下記のようにUPDATABLE JOIN VIEWを使えばもう少し効率的に処理することができます。
[UPDATABLE JOIN VIEWに変更したSQL]
UPDATE /*+ BYPASS_UJVC */
( SELECT b.dname, a.ename, a.deptno
FROM emp a, dept b
WHERE a.deptno = b.deptno(+)
AND a.empno > 0
もし、バージョンが10g以上である場合にはMERGE構文を使って効率的な処理ができます。
[MERGE構文に変更した後PALALLEL DMLを使うように変更したSQL]
ALTER SESSION ENABLE PARALLEL DML;
MERGE /*+ PARALLEL(A 2) USE_HASH(B) */ INTO emp a
USING (
SELECT /*+ FULL(C) PARALLEL(C 2) */
c.dname, c.deptno
FROM dept c
) b
ON (
a.deptno = b.deptno(+) and a.empno > 0
)
WHEN MATCHED THEN
UPDATE SET a.ename = b.dname ;
上記の改善SQLのようにOracle 10gからはMERGE構文で処理できるトランザクションの方式が多様化して活用幅はより一層大きくなったと考えられます。
もう少し本格的にMERGE構文を理解するために構文の構成要素に対して調べてみることにしましょう。
構成要素 | 内容 |
INTO句 | MERGE INTO merge_t1 tt |
USING句 | USING ( SELECT c1, c2, c3 FROM MERGE_T2 WHERE c1 >= 99990 AND c1 <= 100090 ) st |
ON句 | ON (tt.c1 = st.c1) WHEN MATCHED THEN UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3 DELETE WHERE (tt.c2 = ‘A’) WHEN NOT MATCHED THEN INSERT (tt.c1, tt.c2, tt.c3) values (st.c1, st.c2, st.c3) WHERE (st.c2 = ‘A’); |
テストのために下のスクリプトを実行することにしましょう。
Script. MERGE構文テスト用
< MERGE_T1 >
■生成要件
-テーブル件数は100,000ロー
-カラムC1は値の種類が100,000種類であり、Uniqueハム。(値の範囲1~1,000,000)
-カラムC2は値の種類が26種類でありアルファベットである。
-カラムC3は値の種類が100,000種類でありUniqueする。 (値の範囲100,000~199,999)
■テーブル生成
create table merge_t1
as
select level as c1, chr(65+mod(level,26)) as c2, level+99999 as c3
from DUAL
connect by level <= 100000 ;
■各カラムにインデックス生成および統計情報収集
create index merge_t1_idx_01 on merge_t1 ( c1 ) ;
exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'merge_t1',cascade=>TRUE,estimate_percent=>100) ;
< MERGE_T2 >
■生成要件
-テーブル件数は500,000ロー
-カラムC1は値の種類が500,000種類であり、Uniqueする。 (値の範囲1~1000,000)
-カラムC2は値の種類が26種類でありアルファベットである。
-カラムC3は値の種類が10種類でありNULLデータ存在. (値の範囲1~9,null)
■テーブル生成
create table merge_t2
as
select level as c1, chr(65+mod(level,26)) as c2, decode(mod(level,10),0,null,mod(level,10)) as c3
from DUAL
connect by level <= 500000 ;
■各カラムにインデックス生成および統計情報収集
create index merge_t2_idx_01 on merge_t2 ( c1 ) ;
exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'merge_t2',cascade=>TRUE,estimate_percent=>100) ;
5.1 MERGE構文の構成要素が分かること
INTO節
NTO句は大きく2つの役割を担いますが、まずはTarget Tableを定義することができ、2つ目はヒント構文を適用できるということです。 Target TableとはUPDATE、DELETE、INSERTを実行する対象テーブルを意味し、1つのテーブルのみ記述が可能です。 また、MERGE 構文でヒント構文を適用することができるますが、PARALLEL ヒント、Source/Target 結合順序決定ヒント、Source Table との結合方式決定ヒント、Target Table のアクセスタイプ決定ヒントなど、ほとんどのヒントを適用することができます。 ヒントの適用方法はMERGEとINTOの間で定義できますが、以下のヒントを使った例題を見てみましょう。
[ヒント構文適用例]
MERGE /*+ LEADING(ST) USE_NL(ST TT) INDEX(TT) */ INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
MERGE構文ではDMLまで並列処理を実行できますが、PARALLEL DMLを適用するためにはセッションでPARALLEL DMLが支援されなければならないので、以下の命令語でENABLEさせた後、PARALLELヒントを付与してこそ並列処理されるのです。
ALTER SESSION ENABLE PARALLEL DML;---> PARALLEL DMLを実行できるように設定
MERGE /*+ LEADING(ST) USE_HASH(ST TT) FULL(TT) PARALLEL(TT 4) */ INTO MERGE_T1 tt
……以下省略……
USING節
SING節はSource Tableを指定して、Target TableにUPDATE,INSERTを実行する対象データを抽出する部分です。 注意する点は抽出データ カラムのうちON節でTarget Tableと結合するカラムの値を必ずUniqueしなければならないことです。 そしてUSING節でもINTO節と同じようにヒント構文を適用することができます。
ON節
ON節はTarget TableのデータのうちSource Tableで抽出されたデータと一致するデータなのかチェックした後、一致すれば(WHEN MATCHED THEN) UPDATEとDELETEを実行し、一致しなければ(WHEN NOT MATCHED THEN) INSERTを実行するように設定する部分です。
下記のようにON節は大きく三部分に分かれます。
1. ON (Target_Table.Column = Source_Table.Column) JOIN Condition
2. WHEN MATCHED THEN UPDATE or UPDATE & DELETE
3. WHEN NOT MATCHED THEN INSERT
三部分中2,3部分は2→3,3→ 2とともに技術順序に関係なくて、2も3二つのうち一つだけ記述してもかまわない。 以降でテストを通じて確認してみることにしましょう。
5.2MERGE構文で処理されるデータを理解すること
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
COMMIT;-- Merge構文はトランザクションを反映するためにはCOMMITを実行しなければならない。
これからMERGE構文を通じてデータがどのように処理されるのかに対して詳しく調べてみることにしましょう。
MERGE_T1テーブでは何件UPDATEされるか?
Target TableであるMERGE_T1テーブルにUPDATEされるデータはSource Tableで抽出されたデータのうちTarget Tableに存在するデータ(ON(tt.c1 = st.c1) WHEN MATCHED THEN)です。 MERGE実行前/後データを比較してUPDATEされるデータを確認してみることにしましょう。
SELECT COUNT(*)
FROM (
SELECT c1 ,
c2 ,
c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100000
) st ,
MERGE_T1 tt
WHERE tt.c1 = st.c1 ;
COUNT(*)
---------
11 --->合計11件がUPDATE対象データである。
[TARGET TABLEを問い合わせてデータ確認]
SELECT c1, c2, c3
FROM MERGE_T1
WHERE c1 >= 99990
AND c1 <= 100000 ;
MERGE実行前 | MERGE実行後 |
C1 C2 C3 ——– — ——— 99990 U 199989 99991 V 199990 99992 W 199991 99993 X 199992 99994 Y 199993 99995 Z 199994 99996 A 199995 99997 B 199996 99998 C 199997 99999 D 199998 100000 E 199999 合計11件アップデート対象 | C1 C2 C3 ——– — ——— 99990 U 99991 V 1 99992 W 2 99993 X 3 99994 Y 4 99995 Z 5 99997 B 7 99998 C 8 99999 D 9 100000 E 合計10件アップデート、1件削除 |
ON節のTT.C1 = ST.C1条件から抽出されたデータは合計11件で、C1の値は99990 ~ 100000です。 “MERGE実行後”結果を見れば、MERGE_T1.C1 = 99996データが存在せず、残りのデータは全部C3値が変更されていることを確認することができます。 MERGE_T1テーブルでC1値が99996値のデータはDELETE WHEN (tt.c2 = ‘A’)条件を満足するデータですので、削除されて“MERGE実行後”結果では見られません。
[DELETE条件とMATCH DATA]
SELECT COUNT(*)
FROM MERGE_T1
WHERE c1 >= 99990
AND c1 <= 100000
AND c2 = 'A' ;
COUNT(*)
---------
1 MERGE_T1.C1 = 99996
MERGE_T1テーブルでは何件DELETEされるか?
MERGE_T1テーブルでDELETEされる件数はSource TableデータのうちTarget Tableに存在するデータ(ON(TT.C1 = ST.C1) WHEN MATCHED THEN)を抽出し、DELETE WHERE節の条件をチェックした後、最終対象データを抽出してTarget TableでDELETEを実行します。
SELECT COUNT(*)
FROM MERGE_T1
WHERE c1 < 99990 --->トランザクション対象でないデータは変化がなし。
AND c2 = 'A' ;
MERGE実行前 | MERGE実行後 |
COUNT(*) ———- 3845 | COUNT(*) ———- 3845 |
SELECT COUNT(*)
FROM MERGE_T1
WHERE c1 >= 99990
AND c1 <= 100000
AND c2 = ‘A’;---> MERGE構文のDELETE時チェック条件
MERGE実行前 | MERGE実行後 |
COUNT(*) ——— 1 | COUNT(*) ——— 0 → 1件DELETE |
MERGE構文実行前MERGE_T1のC1値は100,000まで存在したのでMERGE_T2で使われた抽出条件に合うデータを比較してみれば上記に示す通り合計1件がDELETEされました。
MERGE_T1テーブルでは何件INSERTされるか?
MERGE_T1テーブルにINSERTされるデータはSource TableデータのうちTarget Tableに存在しないデータ(ON(tt.c1 = st.c1) WHEN NOT MATCHED THEN)を抽出し、INSERT WHERE節の条件をチェックした後、最終対象データを抽出してTarget TableにINSERTを実行します。 MERGE構文実行前MERGE_T1テーブルはC1値が100,000までのデータだけあったので、新規INSERTデータ件数確認は以下のSQLですることができます。
SELECT c1,c2,c3
FROM MERGE_T1
WHERE c1 > 100000 ;
MERGE実行前 | MERGE実行後 |
no rows selected | C1 C2 C3 ————- — ———- 100022 A 2 100048 A 8 100074 A 4 |
SELECT COUNT(c2)
FROM MERGE_T2
WHERE c1 > 100000
AND c1 <= 100090
AND c2 = 'A' ;
COUNT(C2)
----------
3 → INSERT対象が合計3件
5.3MERGE構文作成時発生しうるエラーと解決方法を調べる
MERGE構文作成時発生するエラーはカラムの性格によって発生する場合が多くあります。 まず、以下のMERGE_T1課MERGE_T2のカラム情報を見回してみることにしましょう。
MERGE_T1テーブルの全体件数は10万件ですが、C1とC3カラムのDistinct Valueが10万でUniqueな値であることがわかります。 そしてMERGET_T2テーブルの全体件数は50万件ですが、C1カラムのDistinct Valueが50万でUniquな値だということがわかります。
[TABLE (Column) STATISTICS]
Table : merge_t1
DATA PREC NUMBER
COLUMN_NAME DATA_TYPE LEN SCAL N DISTINCT NUM_NULLS
------------ ---------- ----- ---- - ---------- ----------
C1 NUMBER 22 Y 100000 0
C2 VARCHAR2 2 Y 26 0
C3 NUMBER 22 Y 100000 0
Table : merge_t2
DATA PREC NUMBER
COLUMN_NAME DATA_TYPE LEN SCAL N DISTINCT NUM_NULLS
------------ ---------- ----- ---- - ---------- ----------
C1 NUMBER 22 Y 500000 0
C2 VARCHAR2 2 Y 26 0
C3 NUMBER 22 Y 9 50000
TARGET TABLEとSOURCE TABLEの結合は1:1でなければならない
以下のエラーはMERGE構文を作成する時たくさん発生します。 このエラーはON節に結合条件で使うカラムはUniqueある値であってこそ避けることができるのです。 以下のエラー発生例を通じて内容を確認して解決方法を探してみることにしましょう。
エラー発生例[1]. 重複データ生成でON節のMERGE_T1,MERGE_T2兆である処理が1:Nである場合
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2, (SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 2)
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
MERGE INTO merge_t1 tt
*
ERROR at line 1:
ORA-30926: unable to get a stable set of rows in the source tables
エラー発生例[2]. ON節のMERGE_T1,MERGE_T2兆である処理がN:Nである場合
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c2 = st.c2 )
WHEN MATCHED THEN
UPDATE SET tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
MERGE INTO merge_t1 tt
*
ERROR at line 1:
ORA-30926: unable to get a stable set of rows in the source tables
エラー発生例[1],[2]でエラーが発生したMERGE構文を正常実行するためには下記のようにSQLを変更したり、結合カラムを変更しなければなりません。
・ エラー発生例[1]. Source Tableでデータ抽出時DISTINCTやGROUP BY処理必要
・ エラー発生例[2]. ON節の結合連結カラム確認後変更
それではエラー発生例[1]で発生したエラーを下記のようにSource TableでDISTINCTを追加して解決してみましょう。
MERGE INTO MERGE_T1 tt
USING (
SELECT DISTINCT c1, c2, c3
FROM MERGE_T2,(SELECT LEVEL FROM DUAL CONNECT BY level <= 2)
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
14 rows merged.
UPDATEカラムはON節に使うことはできない
MERGE構文を作成してみるならば、ON節(結合連結)で使ったカラムをUPDATEまで実行しなければならない場合があります。 しかし、このような場合MERGE構文はエラーを発生させます。 なぜなら、MERGE構文でON節に使ったカラムはUPDATEを実行できないためです。 もし、上のような場合、ON節に使うカラムがPrimary Keyカラム(または、Uniqueデータを持つカラム)ならば、エラーなしで実行するためにOracleが提供するROWIDを利用すれば簡単に処理することができます。 以下ののテストを通じて調べてみることにしましょう。
[エラー発生例]
MERGE INTO MERGE_T1 tt
USING (
SELECT c1, c2, c3
FROM MERGE_T2
WHERE c1 >= 99990
AND c1 <= 100090
) st
ON ( tt.c1 = st.c1 )
WHEN MATCHED THEN
UPDATE SET tt.c1 = st.c1, tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
tt.c1 = st.c1
*
ERROR at line 9:
ORA-38104:Columns referenced in the ON節cannot be updated:"TT"。"C1"
[ROWIDを利用して正常実行した例]
MERGE INTO MERGE_T1 tt
USING (
SELECT st.c1, st.c2, st.c3, tt.ROWID as rid
FROM MERGE_T2 st, MERGE_T1 tt
WHERE st.c1 >= 99990
AND st.c1 <= 100090
AND st.c1 = tt.c1(+)
) st
ON ( tt.ROWID = st.rid )
WHEN MATCHED THEN
UPDATE SET tt.c1 = st.c1, tt.c2 = st.c2, tt.c3 = st.c3
DELETE WHERE (tt.c2 = 'A')
WHEN NOT MATCHED THEN
INSERT (tt.c1, tt.c2, tt.c3) VALUES (st.c1, st.c2, st.c3)
WHERE (st.c2 = 'A') ;
14 rows merged.
ON節に記述されたC1カラムにUPDATEを実行する場合、エラーが発生しましたが、C1カラムの代わりにROWIDを抽出してC1カラムを代えるので、エラーなしで正常実行されました。
5.4 MERGE構文は多様な方式のDMLを支援する
MERGE構文は10g以後から制約がたくさん消えてほとんどすべての方式のDMLを支援します。 今からはMERGE構文で処理できるDML類型に対して調べてみて、テストを通じて確認してみるようにします。
・ WHEN MATCHED THEN → UPDATE & DELETE
・ WHEN NOT MATCHED THEN → INSERT
MERGE構文はON節にWHEN MATCHED THEN,WHEN NOT MATCHED THEN節を利用して三つ類型のトランザクション処理ができます。
・ CASE1. UPDATE & DELETE & INSERTまたはUPDATE & INSERT (順序関係ない)
・ CASE2. Only INSERT
・ CASE3. Only UPDATE or UPDATE & DELETE
テーブル5-2はOracle 10.2.0.3環境で、三つ類型のトランザクションを実行した結果をしまします。
テーブル5-2. MERGE構文を利用したトランザクション類型別実行結果
CASE | SQL | 実行結果 |
CASE[1] | merge into merge_t1 tt using ( select st.c1,st.c2,st.c3 from merge_t2 st where st.c1 >= 99990 and st.c1 <= 100090 ) st on ( tt.c1 = st.c1 ) when matched then update set tt.c2 = st.c2, tt.c3 = st.c3 delete where (tt.c2 = ‘A’) when not matched then insert (tt.c1, tt.c2, tt.c3) values (st.c1, st.c2, st.c3) where (st.c2 = ‘A’) ; | 正常実行 |
CASE[1] | merge into merge_t1 tt using ( select st.c1,st.c2,st.c3 from merge_t2 st where st.c1 >= 99990 and st.c1 <= 100090 ) st on ( tt.c1 = st.c1 ) when not matched then insert (tt.c1, tt.c2, tt.c3) values (st.c1, st.c2, st.c3) where (st.c2 = ‘A’) when matched then update set tt.c2 = st.c2, tt.c3 = st.c3 delete where (tt.c2 = ‘A’) ; | 正常実行 |
CASE[2] | merge into merge_t1 tt using ( select st.c1,st.c2,st.c3 from merge_t2 st where st.c1 >= 99990 and st.c1 <= 100090 ) st on ( tt.c1 = st.c1 ) when not matched then insert (tt.c1, tt.c2, tt.c3) values (st.c1, st.c2, st.c3) where (st.c2 = ‘A’) ; | 正常実行 |
CASE[3] | merge into merge_t1 tt using ( select st.c1,st.c2,st.c3 from merge_t2 st where st.c1 >= 99990 and st.c1 <= 100090 ) st on ( tt.c1 = st.c1 ) when matched then update set tt.c2 = st.c2, tt.c3 = st.c3 delete where (tt.c2 = ‘A’) ; | 正常実行 |
5. MERGE構文を性能問題に活用しよう
テーブル5-3. アプリケーション ロジックにともなうMERGE構文活用
CASE(1) | CASE(2) | CASE(3) | |
(1) | Cursor Open SELECT Fetch | Cursor Open SELECT Fetch | FILE OPEN SELECT Fetch Row |
(2) | FOR IF CNT > 0 THEN UPDATE ELSE INSERT; END; END LOOP; | FOR UPDATE or INSERT END LOOP; | FOR UPDATE or INSERT END LOOP; |
(3) | Cursor Close | Cursor Close | Cursor Close |
CASE(1),CASE(2),CASE(3)はCURSOR (Source Table)でデータを抽出し、抽出されたデータ件数だけにFOR文を反復実行し、UPDATE & INSERT構文を実行するパターンのプログラムです。 CASE(1),CASE(2)と違うようにCASE(3)の場合は物理ファイルを読んでファイルのあるROWずつFETCHしてUPDATE構文を実行しますが、これはFILE OPEN + FETCH ROW部分がCASE(1),CASE(2)のCURSOR部分と同一だと考えれば良いです。 このようなパターンのプログラムは普通多くのデータを処理する配置プログラムで使われます。 そしてプログラムの性能はCURSORで抽出されるデータ件数によって決定される場合が大部分です。 それでこのような場合、以前で調べてみたMERGE構文を活用すれば、実行時間を大きく短縮させることができます。
MERGE構文はOracle 9iまでUPSERT (UPDATE+INSERT)構文だけで使われていましたが、10gからはOnly UPDATE,Only INSERT,UPSERTなど多様なパターンのDMLを処理することができるようにMERGE構文の活用幅が広くなり、CASE(1),CASE(2),CASE(3)のようなパターンの配置プログラム性能を改善するための目的でたくさん使われています。 ただ、CASE(3)の場合は物理ファイルを読んでUPDATEでもINSERTを反復実行するプログラムで、Oracle DWチューニングのために9iに新しく出てきた機能であるEXTERNAL TABLEとMERGE構文を共に利用すれば性能を改善することができます。