MERGE 文は、更新操作を実行し、大量のテーブルデータを更新するために使用します。データのマージ時に、ソースデータのローがターゲットデータのローに一致する場合または一致しない場合に実行するアクションを指定できます。
MERGE 文の構文の省略形を以下に示します。
MERGE INTO target-object USING source-object ON merge-search-condition { WHEN MATCHED | WHEN NOT MATCHED } [...]
データベースによってマージ操作が実行されると、source-object のローと target-object のローが比較され、ON 句に含まれる定義に基づいて一致する行または一致しない行が検索されます。merge-search-condition が true になるローが少なくとも 1 つ target-table に存在する場合、source-object のローは一致と見なされます。
source-object は、ベーステーブル、ビュー、マテリアライズドビュー、派生テーブル、プロシージャーの結果のいずれかです。target-object には、これらのオブジェクトのうち、マテリアライズドビューとプロシージャー以外の任意のオブジェクトを指定できます。
ANSI SQL/2008 標準では、マージ操作中に target-object のローを source-object の複数のローで更新することは許可されません。
source-object のローが一致または不一致と見なされると、それぞれ一致の場合と不一致の場合の WHEN 句 (WHEN MATCHED または WHEN NOT MATCHED) に対して評価されます。WHEN MATCHED 句では、target-object のローに対して実行するアクションを定義します (たとえば、WHEN MATCHED ... UPDATE は、target-object のローを更新することを指定します)。WHEN NOT MATCHED 句では、source-object の一致しないローを使用して、target-object に対して実行するアクションを定義します。
WHEN 句は無制限に指定でき、指定した順序で処理されます。また、WHEN 句内で AND 句を使用し、ローのサブセットに対するアクションを指定することもできます。たとえば、次の WHEN 句では、一致したローの Quantity カラムの値に応じて、異なるアクションを実行するよう定義しています。
WHEN MATCHED AND myTargetTable.Quantity<=500 THEN SKIP WHEN MATCHED AND myTargetTable.Quantity>500 THEN UPDATE SET myTargetTable.Quantity=500 |
一致するローおよび一致しないローをアクションごとにグループ化することを「分岐化」と呼び、各グループを「分岐」と呼びます。「分岐」は、単一の WHEN MATCHED 句または WHEN NOT MATCHED 句と同等です。たとえば、ある分岐に source-object の一致しないローのセットが含まれている場合は、それらのローを挿入する必要があります。分岐アクションの実行は、すべての分岐アクティビティを完了してから (source-object のすべてのローを評価してから) 開始されます。データベースサーバーは、WHEN 句が指定された順序に従って、分岐アクションの実行を開始します。
source-object の一致しないロー、または source-object と target-object の一致するローのペアが分岐に入ると、後続の分岐に対して評価されません。したがって、WHEN 句を指定する順序は重要です。
一致または不一致と見なされる source-object のローのうち、いずれの分岐にも属さない (つまり、いずれの WHEN 句も満たさない) ものは無視されます。これは、WHEN 句に AND 句が含まれていて、ローがいずれの AND 句の条件も満たさない場合に発生します。この場合、アクションが定義されていないため、ローは無視されます。
データを変更するアクションは、個々の INSERT、UPDATE、DELETE 文としてトランザクションログに記録されます。
通常、マージ操作中に各 INSERT、UPDATE、DELETE 文を実行すると、トリガーが起動されます。たとえば、UPDATE アクションが定義された分岐の処理時に、データベースサーバーは次の内容を実行します。
すべての BEFORE UPDATE トリガーを起動する
ローの候補セットに対して UPDATE 文を実行すると同時に、すべてのローレベルの UPDATE トリガーを起動する
AFTER UPDATE トリガーを起動する
target-table に対してトリガーを起動すると、別の分岐で更新されるローに影響が及ぶ場合、マージ操作で競合が発生する可能性があります。たとえば、ロー B を削除するトリガーを起動するアクションをロー A に対して実行するとします。しかし、ロー B には独自のアクションが定義されており、まだ実行されていません。ローに対してアクションを実行できないと、マージ操作は失敗し、すべての変更がロールバックされ、エラーが返されます。
複数のトリガーアクションが定義されたトリガーは、同じトリガーに各トリガーアクションを 1 つずつ指定したものと見なされます (つまり、各トリガーに 1 つのトリガーアクションを指定して、別個のトリガーを定義したのと同等になります)。
MERGE 文によって多数のローを更新すると、データベースサーバーのパフォーマンスに影響する場合があります。多数のローを更新する場合は、従属する即時マテリアライズドビューのデータをトランケートしてから、テーブルで MERGE 文を実行することを検討してください。MERGE 文を実行したら、REFRESH MATERIALIZED VIEW 文を実行します。
MERGE 文によって多数のローを更新すると、データベースサーバーのパフォーマンスに影響する場合があります。テーブルで MERGE 文を実行する際は、先に従属するテキストインデックスを削除することを検討してください。MERGE 文を実行したら、テキストインデックスを再作成します。
ジャケットとセーターを販売する小さい会社を経営しているとします。ジャケットの素材の価格が 5% 上昇したため、それに合わせて価格を調整したいとします。次の CREATE TABLE 文を使用して、販売するジャケットとセーターの現在の価格情報を保持する myProducts という小さいテーブルを作成します。その後の INSERT 文で、myProducts にデータを入力します。
CREATE TABLE myProducts ( product_id NUMERIC(10), product_name CHAR(20), product_size CHAR(20), product_price NUMERIC(14,2)); INSERT INTO myProducts VALUES (1, 'Jacket', 'Small', 29.99); INSERT INTO myProducts VALUES (2, 'Jacket', 'Medium', 29.99); INSERT INTO myProducts VALUES (3, 'Jacket', 'Large', 39.99); INSERT INTO myProducts VALUES (4, 'Sweater', 'Small', 18.99); INSERT INTO myProducts VALUES (5, 'Sweater', 'Medium', 18.99); INSERT INTO myProducts VALUES (6, 'Sweater', 'Large', 19.99); SELECT * FROM myProducts; |
product_id | product_name | product_size | product_price |
---|---|---|---|
1 | Jacket | Small | 29.99 |
2 | Jacket | Medium | 29.99 |
3 | Jacket | Large | 39.99 |
4 | Sweater | Small | 18.99 |
5 | Sweater | Medium | 18.99 |
6 | Sweater | Large | 19.99 |
さらに、次の文を使用して、ジャケットの価格変更に関する情報を保持する myPrices という別のテーブルを作成します。マージ操作を実行する前に myPrices テーブルの内容を確認できるように、末尾に SELECT 文を追加します。
CREATE TABLE myPrices ( product_id NUMERIC(10), product_name CHAR(20), product_size CHAR(20), product_price NUMERIC(14,2), new_price NUMERIC(14,2)); INSERT INTO myPrices (product_id) VALUES (1); INSERT INTO myPrices (product_id) VALUES (2); INSERT INTO myPrices (product_id) VALUES (3); INSERT INTO myPrices (product_id) VALUES (4); INSERT INTO myPrices (product_id) VALUES (5); INSERT INTO myPrices (product_id) VALUES (6); COMMIT; SELECT * FROM myPrices; |
product_id | product_name | product_size | product_price | new_price |
---|---|---|---|---|
1 | (NULL) | (NULL) | (NULL) | (NULL) |
2 | (NULL) | (NULL) | (NULL) | (NULL) |
3 | (NULL) | (NULL) | (NULL) | (NULL) |
4 | (NULL) | (NULL) | (NULL) | (NULL) |
5 | (NULL) | (NULL) | (NULL) | (NULL) |
6 | (NULL) | (NULL) | (NULL) | (NULL) |
次の MERGE 文を使用して、myProducts テーブルのデータを myPrices テーブルにマージします。source-object は、product_name が Jacket のローのみを含むようフィルタリングされた派生テーブルです。また、ON 句では、target-object と source-object の product_id カラムの値が一致する場合にローが一致するよう指定しています。
MERGE INTO myPrices p USING ( SELECT product_id, product_name, product_size, product_price FROM myProducts WHERE product_name='Jacket') pp ON (p.product_id = pp.product_id) WHEN MATCHED THEN UPDATE SET p.product_id=pp.product_id, p.product_name=pp.product_name, p.product_size=pp.product_size, p.product_price=pp.product_price, p.new_price=pp.product_price * 1.05; SELECT * FROM myPrices; |
product_id | product_name | product_size | product_price | new_price |
---|---|---|---|---|
1 | Jacket | Small | 29.99 | 31.49 |
2 | Jacket | Medium | 29.99 | 31.49 |
3 | Jacket | Large | 39.99 | 41.99 |
4 | (NULL) | (NULL) | (NULL) | (NULL) |
5 | (NULL) | (NULL) | (NULL) | (NULL) |
6 | (NULL) | (NULL) | (NULL) | (NULL) |
product_id が 4、5、6 のカラムの値は、NULL のままになります。これは、myProducts テーブルで製品が (product_name='Jacket'
) であるいずれのローとも一致しなかったためです。
次の例では、myTargetTable のプライマリキー値を使用してローを一致させ、mySourceTable テーブルと myTargetTable テーブルのローをマージします。mySourceTable のローに、myTargetTable のプライマリキーカラムと同じ値が含まれている場合、ローは一致していると見なされます。
MERGE INTO myTargetTable USING mySourceTable ON PRIMARY KEY WHEN NOT MATCHED THEN INSERT WHEN MATCHED THEN UPDATE; |
WHEN NOT MATCHED THEN INSERT 句は、mySourceTable にはあって myTargetTable にはないローを、myTargetTable に追加することを指定します。WHEN MATCHED THEN UPDATE 句は、myTargetTable の一致するローを mySourceTable の値に更新することを指定します。
次の構文は前述の構文と同等です。ここでは、myTargetTable にはカラム (I1, I2, .. In) があり、プライマリキーがカラム (I1, I2) に定義されていることを前提としています。mySourceTable にはカラム (U1, U2, .. Un) があります。
MERGE INTO myTargetTable ( I1, I2, .. ., In ) USING mySourceTable ON myTargetTable.I1 = mySourceTable.U1 AND myTargetTable.I2 = mySourceTable.U2 WHEN NOT MATCHED THEN INSERT ( I1, I2, .. In ) VALUES ( mySourceTable.U1, mySourceTable.U2, ..., mySourceTable.Un ) WHEN MATCHED THEN UPDATE SET myTargetTable.I1 = mySourceTable.U1, myTargetTable.I2 = mySourceTable.U2, ... myTargetTable.In = mySourceTable.Un; |
一致または不一致のアクションに指定できるアクションの 1 つが、RAISERROR です。RAISERROR を使用すると、WHEN 句の条件を満たした場合に、マージ操作を失敗させることができます。
RAISERROR を指定すると、データベースサーバーはデフォルトで SQLSTATE 23510 および SQLCODE -1254 を返します。必要に応じて、RAISERROR キーワードの後に error_number パラメーターを指定し、返される SQLCODE をカスタマイズできます。
カスタム SQLCODE の指定は、後でエラーの発生した状況を特定する場合に便利です。
カスタム SQLCODE には 17000 よりも大きい正の整数を指定してください。数または変数のいずれとしても指定できます。
次の文で、SQLCODE のカスタマイズによって返される内容にどのような影響が出るかを簡単に示します。
次のように targetTable を作成します。
CREATE TABLE targetTable( c1 int ); INSERT INTO targetTable VALUES( 1 ); COMMIT; |
次の文は、SQLSTATE = '23510' および SQLCODE = -1254 のエラーを返します。
MERGE INTO targetTable USING (SELECT 1 c1 ) AS sourceData ON targetTable.c1 = sourceData.c1 WHEN MATCHED THEN RAISERROR; SELECT sqlstate, sqlcode; |
次の文は、SQLSTATE = '23510' および SQLCODE = -17001 のエラーを返します。
MERGE INTO targetTable USING (SELECT 1 c1 ) AS sourceData ON targetTable.c1 = sourceData.c1 WHEN MATCHED THEN RAISERROR 17001 WHEN NOT MATCHED THEN RAISERROR 17002; SELECT sqlstate, sqlcode; |
次の文は、SQLSTATE = '23510' および SQLCODE = -17002 のエラーを返します。
MERGE INTO targetTable USING (SELECT 2 c1 ) AS sourceData ON targetTable.c1 = sourceData.c1 WHEN MATCHED THEN RAISERROR 17001 WHEN NOT MATCHED THEN RAISERROR 17002; SELECT sqlstate, sqlcode; |
![]() |
DocCommentXchange で意見交換できます
|
Copyright © 2012, iAnywhere Solutions, Inc. - SQL Anywhere 12.0.1 |