
Oracle 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]のようにSET節にサブクエリを使った場合、Where節の条件を実行後抽出されるデータ件数が多いならば、反復的なサブクエリの実行で性能問題が発生するでしょう。
このような場合、下記のようにUPDATABLE JOIN VIEWを使えばもう少し効率的に処理することができます。
もし、バージョンが10g以上である場合にはMERGE構文を使って効率的な処理ができます。
上記の改善SQLのようにOracle 10gからはMERGE構文で処理できるトランザクションの方式が多様化して活用幅はより一層大きくなったと考えられます。
もう少し本格的にMERGE構文を理解するために構文の構成要素に対して調べてみることにしましょう。
構成要素 |
内容 |
INTO句 |
MERGE INTO merge_t1 tt |
USING句 |
USING ( |
ON句 |
ON (tt.c1 = st.c1) |
テストのために下のスクリプトを実行することにしましょう。
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の間で定義できますが、以下のヒントを使った例題を見てみましょう。
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節は大きく三部分に分かれます。
三部分中2,3部分は2→3,3→ 2とともに技術順序に関係なくて、2も3二つのうち一つだけ記述してもかまわない。 以降でテストを通じて確認してみることにしましょう。
5.2MERGE構文で処理されるデータを理解すること
これからMERGE構文を通じてデータがどのように処理されるのかに対して詳しく調べてみることにしましょう。
MERGE_T1テーブでは何件UPDATEされるか?
Target TableであるMERGE_T1テーブルにUPDATEされるデータはSource Tableで抽出されたデータのうちTarget Tableに存在するデータ(ON(tt.c1 = st.c1) WHEN MATCHED THEN)です。 MERGE実行前/後データを比較してUPDATEされるデータを確認してみることにしましょう。
MERGE実行前 |
MERGE実行後 |
C1 C2 C3 |
C1 C2 C3 |
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実行後”結果では見られません。
MERGE_T1テーブルでは何件DELETEされるか?
MERGE_T1テーブルでDELETEされる件数はSource TableデータのうちTarget Tableに存在するデータ(ON(TT.C1 = ST.C1) WHEN MATCHED THEN)を抽出し、DELETE WHERE節の条件をチェックした後、最終対象データを抽出してTarget TableでDELETEを実行します。
MERGE実行前 |
MERGE実行後 |
COUNT(*) |
COUNT(*) |
MERGE実行前 |
MERGE実行後 |
COUNT(*) |
COUNT(*) |
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ですることができます。
MERGE実行前 |
MERGE実行後 |
no rows selected |
C1 C2 C3 |
5.3MERGE構文作成時発生しうるエラーと解決方法を調べる
MERGE構文作成時発生するエラーはカラムの性格によって発生する場合が多くあります。 まず、以下のMERGE_T1課MERGE_T2のカラム情報を見回してみることにしましょう。
MERGE_T1テーブルの全体件数は10万件ですが、C1とC3カラムのDistinct Valueが10万でUniqueな値であることがわかります。 そしてMERGET_T2テーブルの全体件数は50万件ですが、C1カラムのDistinct Valueが50万でUniquな値だということがわかります。
TARGET TABLEとSOURCE TABLEの結合は1:1でなければならない
以下のエラーはMERGE構文を作成する時たくさん発生します。 このエラーはON節に結合条件で使うカラムはUniqueある値であってこそ避けることができるのです。 以下のエラー発生例を通じて内容を確認して解決方法を探してみることにしましょう。
エラー発生例[1]. 重複データ生成でON節のMERGE_T1,MERGE_T2兆である処理が1:Nである場合
エラー発生例[1],[2]でエラーが発生したMERGE構文を正常実行するためには下記のようにSQLを変更したり、結合カラムを変更しなければなりません。
- エラー発生例[1]. Source Tableでデータ抽出時DISTINCTやGROUP BY処理必要
- エラー発生例[2]. ON節の結合連結カラム確認後変更
それではエラー発生例[1]で発生したエラーを下記のようにSource TableでDISTINCTを追加して解決してみましょう。
UPDATEカラムはON節に使うことはできない
MERGE構文を作成してみるならば、ON節(結合連結)で使ったカラムをUPDATEまで実行しなければならない場合があります。 しかし、このような場合MERGE構文はエラーを発生させます。 なぜなら、MERGE構文でON節に使ったカラムはUPDATEを実行できないためです。 もし、上のような場合、ON節に使うカラムがPrimary Keyカラム(または、Uniqueデータを持つカラム)ならば、エラーなしで実行するためにOracleが提供するROWIDを利用すれば簡単に処理することができます。 以下ののテストを通じて調べてみることにしましょう。
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 |
|
CASE[1] |
merge into merge_t1 tt |
|
CASE[2] |
merge into merge_t1 tt |
|
CASE[3] |
merge into merge_t1 tt |
|
5. MERGE構文を性能問題に活用しよう
テーブル5-3. アプリケーション ロジックにともなうMERGE構文活用
CASE(1) |
CASE(2) |
CASE(3) |
|
(1) |
Cursor Open |
Cursor Open |
FILE OPEN |
(2) |
FOR |
FOR |
FOR |
(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構文を共に利用すれば性能を改善することができます。