トランザクションテーブルまたは Delta テーブルに対して INSERT、UPDATE、または DELETE 操作を実行する必要がある場合、MERGE INTO 機能を使用して、これらの操作を 1 つの SQL ステートメントにまとめることができます。 このステートメントは、ソーステーブルとの結合結果に基づいて、ターゲットトランザクションテーブルに対して挿入、更新、または削除操作を実行します。フルテーブルスキャンは 1 回で済むため、実行効率が向上します。
概要
MaxCompute は DELETE および UPDATE 操作をサポートしています。ただし、複数の INSERT、UPDATE、および DELETE 文を使用してターゲットテーブルに対してバッチ操作を実行するには、複数の SQL 文を記述して複数回のフルテーブルスキャンを実行する必要があります。MaxCompute の MERGE INTO 機能では、1 回のフルテーブルスキャンですべての操作を完了でき、その実行効率は INSERT+UPDATE+DELETE よりも高くなります。
MERGE INTO 操作はアトミックです。ジョブは、その INSERT、UPDATE、および DELETE 操作のすべてが成功した場合にのみ、成功したと見なされます。これらの操作のいずれかが失敗した場合、ジョブ全体が失敗します。
さらに、MERGE INTO は、 INSERT、 UPDATE、および DELETE 操作を個別に実行した際に、一部の操作は成功し、他の操作は失敗して、成功した操作をロールバックできなくなるという問題を防ぎます。
注意事項
MERGE INTO操作を実行する前に、対象のトランザクションテーブルに対する Select 権限と Update 権限が必要です。詳細については、「MaxCompute 権限」をご参照ください。単一の
MERGE INTOステートメントでは、同じ行に対して複数のINSERTまたは UPDATE 操作を実行することはできません。MERGE INTO操作の対象テーブルは、トランザクショナルテーブルでなければなりません。MERGE INTOステートメントは、少なくとも 1 つのmerge_action句を含める必要があります。複数の
not_matched_by_target_clause句を使用する場合、そのうち最大 1 つでsearch_conditionを省略できます。複数の
not_matched_by_source_clause句を使用する場合、そのうち最大 1 つでsearch_conditionを省略できます。複数の
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 merge_condition
{ merge_action } +
merge_action ::= matched_clause | not_matched_by_target_clause | not_matched_by_source_clause
-- ON 条件に基づいて行が一致した場合に実行するアクションを指定します。複数の WHEN MATCHED 句で処理されるデータセットは重複できません。
matched_clause ::= WHEN MATCHED [ AND <search_condition> ] THEN { merge_update_clause | merge_delete_clause }
-- ソースの行がターゲットのどの行とも一致しない場合に実行するアクションを指定します。
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 ::= ブール式
search_condition ::= ブール式
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 | はい | ブール式。 |
search_condition | いいえ |
|
set_clause_list | いいえ | 更新するデータ。このパラメーターは
|
value_list | いいえ | 挿入するデータです。このパラメーターは
|
merge_action | はい |
アクションは、 |
matched_clause | いいえ | ターゲットテーブルの行がソーステーブルの行と一致した場合に実行する |
not_matched_by_target_clause | いいえ | どのターゲット行とも一致しないソース行に対するアクションを指定します。 |
not_matched_by_source_clause | いいえ | 一致するソース行がないターゲット行に対するアクションを指定します。 |
merge_insert_clause | いいえ |
|
例
例 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' の行の電話番号を '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 Table タイプのターゲットテーブル mf_tt6 とソーステーブル mf_delta を作成し、データを挿入します。
MERGE INTOコマンドを実行して、ターゲットテーブルの指定されたパーティション内のデータを更新、挿入、または削除します。-- ターゲットテーブル 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 | +------------+------------+----+----+