すべてのプロダクト
Search
ドキュメントセンター

MaxCompute:MERGE INTO

最終更新日:Sep 22, 2026

トランザクションテーブルまたは 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

いいえ

UPDATE、DELETE、または INSERT アクションの追加条件を指定するブール式です。次のルールに注意してください:

  • 3 つの WHEN 句が存在する場合、UPDATE、DELETE、および INSERT はそれぞれ 1 回のみ指定できます。

  • UPDATE と DELETE の両方が実行される場合、最初の操作には [AND <BOOLEAN expression>] を含める必要があります。

  • WHEN NOT MATCHED 句は、最後の WHEN 句としてのみ記述できます。

set_clause_list

いいえ

更新するデータ。このパラメーターは UPDATE 操作では必須です。

UPDATE の詳細については、「UPDATE」をご参照ください。

value_list

いいえ

挿入するデータです。このパラメーターは INSERT 操作で必須です。

VALUES の詳細については、「VALUES」をご参照ください。

merge_action

はい

merge_action には、MATCHED、NOT MATCHED BY TARGET、NOT MATCHED BY SOURCE の 3 種類があります。

アクションは、merge_condition と search_condition の両方が満たされた場合にのみ行に適用されます。ある行が複数の句の条件を満たす場合、その行には、最初に一致した merge_action のみが実行されます。

matched_clause

いいえ

ターゲットテーブルの行がソーステーブルの行と一致した場合に実行する UPDATE または DELETE アクションを定義します。

not_matched_by_target_clause

いいえ

どのターゲット行とも一致しないソース行に対するアクションを指定します。

not_matched_by_source_clause

いいえ

一致するソース行がないターゲット行に対するアクションを指定します。

merge_insert_clause

いいえ

merge_insert_clause には、value_list、ROW、* の 3 つのオプションがあります。value_list は VALUES 式を使用して挿入するデータを指定します。ROW と * はどちらも、ソーステーブルから一致したデータがターゲットテーブルに直接挿入されることを意味します。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' の行の電話番号を '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 |
    +------------+------------+----+----+