トランザクションテーブルまたは Delta テーブルに対して INSERT、UPDATE、DELETE 操作を実行する必要がある場合、MERGE INTO 機能を使用してこれらの操作を単一の SQL 文にまとめることができます。この文は、ソーステーブルとの結合結果に基づいて、ターゲットのトランザクションテーブルに対して挿入、更新、または削除を実行します。このプロセスでは全表スキャンが 1 回で済むため、実行効率が向上します。
概要
MaxCompute は DELETE および UPDATE 操作をサポートしています。しかし、対象のテーブルに対してバッチ INSERT、UPDATE、および DELETE 操作を実行するには、個別の SQL 文を記述する必要があります。この場合、複数回の全表スキャンが必要になります。MaxCompute の MERGE INTO 機能は、1 回の全表スキャンでこれらすべての操作を実行するため、個別の INSERT、UPDATE、および DELETE 文を実行するよりも効率的です。
MERGE INTO 操作はアトミックです。ジョブは、そのすべての INSERT、UPDATE、DELETE 操作が成功した場合にのみ成功します。いずれかの内部操作が失敗した場合、ジョブ全体が失敗します。
INSERT、UPDATE、DELETE 操作を個別に実行する際に発生しうる問題も、MERGE INTO 文によって防ぐことができます。たとえば、これらの操作を個別に実行し、一部は成功し、他は失敗した場合、成功した変更をロールバックすることはできません。MERGE INTO 文はこの問題を回避します。
適用範囲
MERGE INTO操作には、ターゲットのトランザクションテーブルに対する Select (読み取り) および Update 権限が必要です。詳細については、「MaxCompute の権限」をご参照ください。単一の
MERGE INTO文内で、同じ行に対して複数のINSERTまたは UPDATE 操作を実行しないでください。MERGE INTO文のターゲットテーブルは、トランザクションテーブルである必要があります。MERGE INTO文には、少なくとも 1 つのmerge_actionを含める必要があります。複数の
not_matched_by_target_clause句を使用する場合、search_conditionを省略できる句は最大で 1 つです。複数の
not_matched_by_source_clause句を使用する場合、search_conditionを省略できる句は最大で 1 つです。複数の
matched_clause句を使用する場合、search_conditionを省略できる句は最大で 1 つです。merge_conditionおよびsearch_conditionではサブクエリはサポートされていません。
コマンドフォーマット
MERGE INTO <target_table> AS <alias_name_t>
USING <source expression | table_name> AS <alias_name_s>
-- ON 句は、ソーステーブルとターゲットテーブルの結合条件を指定します。
ON merge_condition
{ merge_action } +
merge_action ::= matched_clause | not_matched_by_target_clause | not_matched_by_source_clause
-- WHEN MATCHED...THEN 句は、ON 句の結果が true の場合の操作を指定します。複数の WHEN MATCHED...THEN 句で処理されるデータは重複できません。
matched_clause ::= WHEN MATCHED [ AND <search_condition> ] THEN { merge_update_clause | merge_delete_clause }
-- WHEN NOT MATCHED...THEN 句は、ON 句の結果が false の場合の操作を指定します。
not_matched_by_target_clause ::= WHEN NOT MATCHED [BY TARGET] [ AND <search_condition> ] THEN merge_insert_clause
not_matched_by_source_clause ::= WHEN NOT MATCHED BY SOURCE [ AND <search_condition> ] THEN { merge_update_clause | merge_delete_clause }
merge_condition ::= BOOLEAN expression
search_condition ::= BOOLEAN expression
merge_update_clause ::= UPDATE SET <set_clause_list>
merge_delete_clause ::= DELETE
merge_insert_clause ::= INSERT <value_list> | ROW | *パラメーター
パラメーター | 必須 | 説明 |
target_table | はい | ターゲットテーブルの名前。テーブルは存在している必要があります。 |
alias_name_t | いいえ | ターゲットテーブルのエイリアス。 |
source expression|table_name | はい | 結合するソーステーブル、ビュー、またはサブクエリの名前。 |
alias_name_s | いいえ | ソーステーブル、ビュー、またはサブクエリのエイリアス。 |
merge_condition | はい | BOOLEAN 値を返す条件式。結果は True または False である必要があります。 |
search_condition | いいえ |
|
set_clause_list | いいえ | 更新するデータ。このパラメーターは
|
value_list | いいえ | 挿入するデータ。これは
|
merge_action | はい | `merge_action` には、MATCHED、NOT MATCHED BY TARGET、NOT MATCHED BY SOURCE の 3 つのオプションがあります。 各 `merge_action` には、オプションの検索条件 (`search_condition`) を含めることができます。`merge_action` は、マージ条件 (`merge_condition`) と検索条件 (`search_condition`) の両方が満たされた場合にのみ行に対して実行されます。複数の句が一致した場合、その行に対しては最初に一致した `merge_action` のみ実行されます。 |
matched_clause | いいえ | `matched_clause` には UPDATE と DELETE の 2 つのオプションがあり、ソーステーブルの行と一致した場合にターゲットテーブルの行を更新または削除する方法を定義します。 |
not_matched_by_target_clause | いいえ | 一致する行が存在しない場合に、ソーステーブルからターゲットテーブルに行を挿入する方法を定義します。 |
not_matched_by_source_clause | いいえ | ソーステーブルに一致する行がない場合に、ターゲットテーブルの行を更新または削除する方法を定義します。 |
merge_insert_clause | いいえ | `merge_insert_clause` には、value_list、ROW、* の 3 つのオプションがあります。`value_list` オプションは values 式を使用して挿入するデータを指定します。ROW と * オプションは、一致したデータをソーステーブルからターゲットテーブルに直接挿入します。これらのオプションでは、ターゲットテーブルとソーステーブルの列数が同じであり、対応するデータの型に互換性がある必要があります。 |
例
例 1:ターゲットテーブル `acid_address_book_base1` とソーステーブル `tmp_table1` を作成し、データを挿入します。
MERGE INTO操作を実行します。データがON条件を満たす場合、ターゲットテーブルをソーステーブルのデータで更新します。データがON条件を満たさず、ソーステーブルの _event_type_ が I である場合、データをターゲットテーブルに挿入します。以下のサンプルコマンドが使用されます。--ターゲットテーブル acid_address_book_base1 を作成します。 CREATE TABLE IF NOT EXISTS acid_address_book_base1 (id BIGINT,first_name STRING,last_name STRING,phone STRING) PARTITIONED BY(year STRING, month STRING, day STRING, hour STRING) tblproperties ("transactional"="true"); --ソーステーブル tmp_table1 を作成します。 CREATE TABLE IF NOT EXISTS tmp_table1 (id BIGINT, first_name STRING, last_name STRING, phone STRING, _event_type_ STRING); --テストデータをターゲットテーブル acid_address_book_base1 に挿入します。 INSERT OVERWRITE TABLE acid_address_book_base1 PARTITION(year='2020', month='08', day='20', hour='16') VALUES (4, 'nihaho', 'li', '222'), (5, 'tahao', 'ha', '333'), (7, 'djh', 'hahh', '555'); --ターゲットテーブルをクエリして、データ挿入の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM acid_address_book_base1; --以下の結果が返されます。 +------------+------------+------------+------------+------------+------------+------------+------------+ | id | first_name | last_name | phone | year | month | day | hour | +------------+------------+------------+------------+------------+------------+------------+------------+ | 4 | nihaho | li | 222 | 2020 | 08 | 20 | 16 | | 5 | tahao | ha | 333 | 2020 | 08 | 20 | 16 | | 7 | djh | hahh | 555 | 2020 | 08 | 20 | 16 | +------------+------------+------------+------------+------------+------------+------------+------------+ --テストデータをソーステーブル tmp_table1 に挿入します。 INSERT OVERWRITE TABLE tmp_table1 VALUES (1, 'hh', 'liu', '999', 'I'), (2, 'cc', 'zhang', '888', 'I'), (3, 'cy', 'zhang', '666', 'I'),(4, 'hh', 'liu', '999', 'U'), (5, 'cc', 'zhang', '888', 'U'),(6, 'cy', 'zhang', '666', 'U'); --ソーステーブルをクエリして、データ挿入の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM tmp_table1; --以下の結果が返されます。 +------------+------------+------------+------------+--------------+ | id | first_name | last_name | phone | _event_type_ | +------------+------------+------------+------------+--------------+ | 1 | hh | liu | 999 | I | | 2 | cc | zhang | 888 | I | | 3 | cy | zhang | 666 | I | | 4 | hh | liu | 999 | U | | 5 | cc | zhang | 888 | U | | 6 | cy | zhang | 666 | U | +------------+------------+------------+------------+--------------+ --merge into 操作を実行します。 MERGE INTO acid_address_book_base1 AS t USING tmp_table1 as s ON s.id = t.id AND t.year='2020' AND t.month='08' AND t.day='20' AND t.hour='16' WHEN MATCHED THEN UPDATE SET t.first_name = s.first_name, t.last_name = s.last_name, t.phone = s.phone WHEN NOT MATCHED AND (s._event_type_='I') THEN INSERT VALUES(s.id, s.first_name, s.last_name,s.phone,'2020','08','20','16'); --ターゲットテーブルをクエリして、merge into 操作の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM acid_address_book_base1; --以下の結果が返されます。 +------------+------------+------------+------------+------------+------------+------------+------------+ | id | first_name | last_name | phone | year | month | day | hour | +------------+------------+------------+------------+------------+------------+------------+------------+ | 4 | hh | liu | 999 | 2020 | 08 | 20 | 16 | | 5 | cc | zhang | 888 | 2020 | 08 | 20 | 16 | | 7 | djh | hahh | 555 | 2020 | 08 | 20 | 16 | | 1 | hh | liu | 999 | 2020 | 08 | 20 | 16 | | 2 | cc | zhang | 888 | 2020 | 08 | 20 | 16 | | 3 | cy | zhang | 666 | 2020 | 08 | 20 | 16 | +------------+------------+------------+------------+------------+------------+------------+------------+例 2:ターゲットテーブル `acid_target1` とソーステーブル `tmp_table2` を作成し、データを挿入します。
MERGE INTO操作を実行して、次の操作を行います:ソーステーブルの行がターゲットテーブルのどの行とも一致せず、その `name` が 'Nika' である場合、その行をターゲットテーブルに挿入します。ターゲットテーブルの行がソーステーブルのどの行とも一致せず、その `name` が 'Tommy' である場合、その行を削除します。ターゲットテーブルの行がソーステーブルのどの行とも一致せず、その `name` が 'Gene' である場合、その行を更新します。--ターゲットテーブル acid_target1 を作成します。 CREATE TABLE IF NOT EXISTS acid_target1 (id BIGINT, name STRING, phone STRING) tblproperties ("transactional"="true"); --ソーステーブル tmp_table2 を作成します。 CREATE TABLE IF NOT EXISTS tmp_table2 (id BIGINT, name STRING, phone STRING); --テストデータをターゲットテーブル acid_target1 に挿入します。 INSERT OVERWRITE TABLE acid_target1 VALUES (1, 'Tommy', '111'), (2, 'Gene', '222'), (3, 'Abram', '333'); --ターゲットテーブルをクエリして、データ挿入の結果を確認します。 SELECT * FROM acid_target1; --以下の結果が返されます。 +------------+------+-------+ | id | name | phone | +------------+------+-------+ | 1 | Tommy | 111 | | 2 | Gene | 222 | | 3 | Abram | 333 | +------------+------+-------+ --テストデータをソーステーブル tmp_table2 に挿入します。 INSERT OVERWRITE TABLE tmp_table2 VALUES (6, 'Queen', '666'), (7, 'Nika', '777'); --ソーステーブルをクエリして、データ挿入の結果を確認します。 SELECT * FROM tmp_table2; --以下の結果が返されます。 +------------+------+-------+ | id | name | phone | +------------+------+-------+ | 6 | Queen | 666 | | 7 | Nika | 777 | +------------+------+-------+ --merge into 操作を実行します。 --name が 'Nika' の行をソーステーブルからターゲットテーブルに挿入します。 --name が 'Tommy' の行をターゲットテーブルから削除します。 --ターゲットテーブルの name が 'Gene' の行を更新します。phone の値を '222' から '999' に設定します。 MERGE INTO acid_target1 t USING tmp_table2 s ON t.id = s.id WHEN NOT MATCHED BY TARGET AND s.name = 'Nika' THEN INSERT (id, name, phone) VALUES(s.id, s.name, s.phone) WHEN NOT MATCHED BY SOURCE AND t.name = 'Tommy' THEN DELETE WHEN NOT MATCHED BY SOURCE AND t.name = 'Gene' THEN UPDATE SET t.phone = '999'; --ターゲットテーブルをクエリして、merge into 操作の結果を確認します。 SELECT * FROM acid_target1; --以下の結果が返されます。 +------------+------+-------+ | id | name | phone | +------------+------+-------+ | 2 | Gene | 999 | | 7 | Nika | 777 | | 3 | Abram | 333 | +------------+------+-------+例 3:ターゲットテーブル `merge_acid_dp` とソーステーブル `merge_acid_source` を作成し、データを挿入します。パーティションを指定せずに
MERGE INTOコマンドを実行してデータを更新または挿入します。操作はターゲットテーブルのすべてのパーティションに適用されます。--ターゲットテーブル merge_acid_dp を作成します。 CREATE TABLE IF NOT EXISTS merge_acid_dp(c1 BIGINT NOT NULL, c2 BIGINT NOT NULL) PARTITIONED BY (dd STRING, hh STRING) tblproperties ("transactional" = "true"); --ソーステーブル merge_acid_source を作成します。 CREATE TABLE IF NOT EXISTS merge_acid_source(c1 BIGINT NOT NULL, c2 BIGINT NOT NULL, c3 STRING, c4 STRING) lifecycle 30; --テストデータをターゲットテーブル merge_acid_dp に挿入します。 INSERT OVERWRITE TABLE merge_acid_dp PARTITION (dd='01', hh='01') VALUES (1, 1), (2, 2); INSERT OVERWRITE TABLE merge_acid_dp PARTITION (dd='02', hh='02') VALUES (4, 1), (3, 2); --ターゲットテーブルをクエリして、データ挿入の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM merge_acid_dp; --以下の結果が返されます。 +------------+------------+----+----+ | c1 | c2 | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 2 | 2 | 01 | 01 | | 4 | 1 | 02 | 02 | | 3 | 2 | 02 | 02 | +------------+------------+----+----+ --テストデータをソーステーブル merge_acid_source に挿入します。 INSERT OVERWRITE TABLE merge_acid_source VALUES(8, 2, '03', '03'), (5, 5, '05', '05'), (6, 6, '02', '02'); --ソーステーブルをクエリして、データ挿入の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM merge_acid_source; --以下の結果が返されます。 +------------+------------+----+----+ | c1 | c2 | c3 | c4 | +------------+------------+----+----+ | 8 | 2 | 03 | 03 | | 5 | 5 | 05 | 05 | | 6 | 6 | 02 | 02 | +------------+------------+----+----+ --merge into 操作を実行します。 SET odps.sql.allow.fullscan=true; MERGE INTO merge_acid_dp tar USING merge_acid_source src ON tar.c2 = src.c2 WHEN MATCHED THEN UPDATE SET tar.c1 = src.c1 WHEN NOT MATCHED THEN INSERT VALUES(src.c1, src.c2, src.c3, src.c4); --ターゲットテーブルをクエリして、merge into 操作の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM merge_acid_dp; --以下の結果が返されます。 +------------+------------+----+----+ | c1 | c2 | dd | hh | +------------+------------+----+----+ | 6 | 6 | 02 | 02 | | 5 | 5 | 05 | 05 | | 8 | 2 | 02 | 02 | | 8 | 2 | 01 | 01 | | 1 | 1 | 01 | 01 | | 4 | 1 | 02 | 02 | +------------+------------+----+----+例 4:ターゲットテーブル `merge_acid_sp` とソーステーブル `merge_acid_source` を作成し、データを挿入します。
MERGE INTOコマンドを実行して、ターゲットテーブルの指定されたパーティションにデータを更新または挿入します。--ターゲットテーブル merge_acid_sp を作成します。 CREATE TABLE IF NOT EXISTS merge_acid_sp(c1 BIGINT NOT NULL, c2 BIGINT NOT NULL) PARTITIONED BY (dd STRING, hh STRING) tblproperties ("transactional" = "true"); --ソーステーブル merge_acid_source を作成します。 CREATE TABLE IF NOT EXISTS merge_acid_source(c1 BIGINT NOT NULL, c2 BIGINT NOT NULL, c3 STRING, c4 STRING) lifecycle 30; --テストデータをターゲットテーブル merge_acid_sp に挿入します。 INSERT OVERWRITE TABLE merge_acid_sp PARTITION (dd='01', hh='01') VALUES (1, 1), (2, 2); INSERT OVERWRITE TABLE merge_acid_sp PARTITION (dd='02', hh='02') VALUES (4, 1), (3, 2); --ターゲットテーブルをクエリして、データ挿入の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM merge_acid_sp; --以下の結果が返されます。 +------------+------------+----+----+ | c1 | c2 | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 2 | 2 | 01 | 01 | | 4 | 1 | 02 | 02 | | 3 | 2 | 02 | 02 | +------------+------------+----+----+ --テストデータをソーステーブル merge_acid_source に挿入します。 INSERT OVERWRITE TABLE merge_acid_source VALUES(8, 2, '03', '03'), (5, 5, '05', '05'), (6, 6, '02', '02'); --ソーステーブルをクエリして、データ挿入の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM merge_acid_source; --以下の結果が返されます。 +------------+------------+----+----+ | c1 | c2 | c3 | c4 | +------------+------------+----+----+ | 8 | 2 | 03 | 03 | | 5 | 5 | 05 | 05 | | 6 | 6 | 02 | 02 | +------------+------------+----+----+ --merge into 操作を実行します。ON 条件で、更新または挿入操作がターゲットテーブルの dd = '01' および hh = '01' パーティションにのみ適用されるように指定します。 SET odps.sql.allow.fullscan=true; MERGE INTO merge_acid_sp tar USING merge_acid_source src ON tar.c2 = src.c2 AND tar.dd = '01' AND tar.hh = '01' WHEN MATCHED THEN UPDATE SET tar.c1 = src.c1 WHEN NOT MATCHED THEN INSERT VALUES(src.c1, src.c2, src.c3, src.c4); --ターゲットテーブルをクエリして、merge into 操作の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM merge_acid_sp; +------------+------------+----+----+ | c1 | c2 | dd | hh | +------------+------------+----+----+ | 5 | 5 | 05 | 05 | | 6 | 6 | 02 | 02 | | 8 | 2 | 01 | 01 | | 1 | 1 | 01 | 01 | | 4 | 1 | 02 | 02 | | 3 | 2 | 02 | 02 | +------------+------------+----+----+例 5:ターゲットテーブルとして Delta テーブル `mf_tt6` とソーステーブル `mf_delta` を作成し、データを挿入します。指定されたパーティションで
MERGE INTOコマンドを実行して、それらのパーティション内のデータを更新、挿入、または削除します。--ターゲットテーブルとして Transactional table 2.0 mf_tt6 を作成します。 CREATE TABLE IF NOT EXISTS mf_tt6 (pk BIGINT NOT NULL PRIMARY key, val BIGINT NOT NULL) PARTITIONED BY (dd STRING, hh STRING) tblproperties ("transactional"="true"); --テストデータをターゲットテーブル mf_tt6 に挿入します。 INSERT OVERWRITE TABLE mf_tt6 PARTITION (dd='01', hh='02') VALUES (1, 1), (2, 2), (3, 3); INSERT OVERWRITE TABLE mf_tt6 PARTITION (dd='01', hh='01') VALUES (1, 10), (2, 20), (3, 30); --現在のセッションでのみ全表スキャンを有効にします。select 文を実行して mf_tt6 テーブルのデータを表示します。 SET odps.sql.allow.fullscan=true; SELECT * FROM mf_tt6; --以下の結果が返されます。 +------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 10 | 01 | 01 | | 3 | 30 | 01 | 01 | | 2 | 20 | 01 | 01 | | 1 | 1 | 01 | 02 | | 3 | 3 | 01 | 02 | | 2 | 2 | 01 | 02 | +------------+------------+----+----+ --ソーステーブル mf_delta を作成し、テストデータを挿入します。 CREATE TABLE IF NOT EXISTS mf_delta AS SELECT pk, val FROM VALUES (1, 10), (2, 20), (6, 60) t (pk, val); --ソーステーブルをクエリして、データ挿入の結果を確認します。 SELECT * FROM mf_delta; --以下の結果が返されます。 +------+------+ | pk | val | +------+------+ | 1 | 10 | | 2 | 20 | | 6 | 60 | +------+------+ --merge into 操作を実行します。ON 条件で、更新、挿入、または削除操作がターゲットテーブル mf_tt6 の dd = '01' および hh = '02' パーティションにのみ適用されるように指定します。 MERGE INTO mf_tt6 USING mf_delta ON mf_tt6.pk = mf_delta.pk AND mf_tt6.dd='01' AND mf_tt6.hh='02' WHEN MATCHED AND (mf_tt6.pk > 1) THEN UPDATE SET mf_tt6.val = mf_delta.val WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT VALUES (mf_delta.pk, mf_delta.val, '01', '02'); --ターゲットテーブルをクエリして、merge into 操作の結果を確認します。 SET odps.sql.allow.fullscan=true; SELECT * FROM mf_tt6; --以下の結果が返されます。 +------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 10 | 01 | 01 | | 3 | 30 | 01 | 01 | | 2 | 20 | 01 | 01 | | 3 | 3 | 01 | 02 | | 6 | 60 | 01 | 02 | | 2 | 20 | 01 | 02 | +------------+------------+----+----+