2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15
開發版本: devel

MERGE

MERGE — 條件性地插入、更新或刪除表中的行

概要

[ WITH with_query [, ...] ]
MERGE INTO [ ONLY ] target_table_name [ * ] [ [ AS ] target_alias ]
    USING data_source ON join_condition
    when_clause [...]
    [ RETURNING [ WITH ( { OLD | NEW } AS output_alias [, ...] ) ]
                { * | output_expression [ [ AS ] output_name ] } [, ...] ]

where data_source is:

    { [ ONLY ] source_table_name [ * ] | ( source_query ) } [ [ AS ] source_alias ]

and when_clause is:

    { WHEN MATCHED [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING } |
      WHEN NOT MATCHED BY SOURCE [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING } |
      WHEN NOT MATCHED [ BY TARGET ] [ AND condition ] THEN { merge_insert | DO NOTHING } }

and merge_insert is:

    INSERT [( column_name [, ...] )]
        [ OVERRIDING { SYSTEM | USER } VALUE ]
        { VALUES ( { expression | DEFAULT } [, ...] ) | DEFAULT VALUES }

and merge_update is:

    UPDATE SET { column_name = { expression | DEFAULT } |
                 ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
                 ( column_name [, ...] ) = ( sub-SELECT )
               } [, ...]

and merge_delete is:

    DELETE

描述

MERGE 對目標表 target_table_name 中標識的行執行修改操作,使用 data_sourceMERGE 提供了一個單一的SQL語句,可以有條件地 INSERTUPDATEDELETE 行,而這項任務否則需要多個過程語言語句。

首先,MERGE 命令從 data_source 連線到目標表,產生零個或多個候選更改行。對於每個候選更改行,MATCHEDNOT MATCHED BY SOURCENOT MATCHED [BY TARGET] 的狀態僅設定一次,之後 WHEN 子句按指定的順序進行評估。對於每個候選更改行,第一個求值為真的子句將被執行。對於任何候選更改行,最多執行一個 WHEN 子句。

MERGE 操作與同名的常規 UPDATEINSERTDELETE 命令效果相同。這些命令的語法不同,特別是沒有 WHERE 子句,也沒有指定表名。所有操作都引用目標表,儘管可以透過觸發器修改其他表。

當指定 DO NOTHING 時,源行將被跳過。由於操作按指定的順序進行評估,DO NOTHING 在進行更精細的處理之前跳過不感興趣的源行非常方便。

可選的 RETURNING 子句會導致 MERGE 基於插入、更新或刪除的每一行計算並返回值。可以計算使用源表或目標表列的任何表示式,或者使用 merge_action() 函式。預設情況下,當執行 INSERTUPDATE 操作時,使用目標表列的新值;當執行 DELETE 時,使用目標表列的舊值;但也可以顯式請求舊值和新值。RETURNING 列表的語法與 SELECT 的輸出列表完全相同。

沒有單獨的 MERGE 許可權。如果您指定了一個更新操作,您必須對目標表中在 SET 子句中引用的列擁有 UPDATE 許可權。如果您指定了一個插入操作,您必須對目標表擁有 INSERT 許可權。如果您指定了一個刪除操作,您必須對目標表擁有 DELETE 許可權。如果您指定了一個 DO NOTHING 操作,您必須對目標表至少一個列擁有 SELECT 許可權。您還需要對 data_source 的任何列以及在任何 condition(包括 join_condition)或 expression 中引用的目標表列擁有 SELECT 許可權。許可權在語句開始時檢查一次,並且無論是否執行特定的 WHEN 子句都會進行檢查。

如果目標表是物化檢視、外部表或已定義了任何規則,則不支援 MERGE

引數

with_query

WITH 子句允許您指定一個或多個可以在 MERGE 查詢中按名稱引用的子查詢。有關詳細資訊,請參閱 第 7.8 節SELECT。請注意,MERGE 不支援 WITH RECURSIVE

target_table_name

要合併到的目標表或檢視的名稱(可選擇帶模式限定)。如果表名前指定了 ONLY,則僅在命名錶中更新或刪除匹配的行。如果未指定 ONLY,則還會在任何繼承自命名錶的表中更新或刪除匹配的行。可選地,可以在表名後指定 * 來顯式指示包含後代表。ONLY 關鍵字和 * 選項不影響插入操作,插入操作始終只插入到命名錶中。

如果 target_table_name 是一個檢視,它必須是自動可更新且沒有 INSTEAD OF 觸發器,或者它必須為 WHEN 子句中指定的每種操作(INSERTUPDATEDELETE)都有 INSTEAD OF 觸發器。帶規則的檢視不受支援。

target_alias

目標表的替代名稱。提供別名後,它會完全隱藏表的實際名稱。例如,給定 MERGE INTO foo AS fMERGE 語句的其餘部分必須將此表稱為 f 而不是 foo

source_table_name

源表、檢視或轉換表的名稱(可選擇帶模式限定)。如果表名前指定了 ONLY,則僅從命名錶中包含匹配的行。如果未指定 ONLY,則還從任何繼承自命名錶的表中包含匹配的行。可選地,可以在表名後指定 * 來顯式指示包含後代表。

source_query

一個提供要合併到目標表中的行的查詢(SELECT 語句或 VALUES 語句)。有關語法描述,請參閱 SELECT 語句或 VALUES 語句。

source_alias

資料來源的替代名稱。提供別名後,它會完全隱藏表的實際名稱或已發出查詢的事實。

join_condition

join_condition 是一個結果為 boolean 型別(類似於 WHERE 子句)的表示式,它指定 data_source 中的哪些行與目標表中的行匹配。

警告

僅應在 join_condition 中出現嘗試與 data_source 行匹配的目標表列。僅引用目標表列的 join_condition 子表示式可能會影響採取的操作,通常會產生意想不到的結果。

如果指定了 WHEN NOT MATCHED BY SOURCEWHEN NOT MATCHED [BY TARGET] 子句,MERGE 命令將在 data_source 和目標表之間執行 FULL 連線。為了使此工作正常進行,至少一個 join_condition 子表示式必須使用支援雜湊連線的運算子,或者所有子表示式都必須使用支援合併連線的運算子。

when_clause

至少需要一個 WHEN 子句。

WHEN 子句可以指定 WHEN MATCHEDWHEN NOT MATCHED BY SOURCEWHEN NOT MATCHED [BY TARGET]。請注意,SQL標準僅定義 WHEN MATCHEDWHEN NOT MATCHED(後者定義為沒有匹配的目標行)。WHEN NOT MATCHED BY SOURCE 是對SQL標準的擴充套件,與 WHEN NOT MATCHED 後面可以附加 BY TARGET 的選項一樣,是為了使其含義更明確。

如果 WHEN 子句指定 WHEN MATCHED,並且候選更改行與 data_source 中的某行與目標表中的某行匹配,則當 condition 缺失或其計算結果為 true 時,將執行 WHEN 子句。

如果 WHEN 子句指定 WHEN NOT MATCHED BY SOURCE,並且候選更改行代表目標表中與 data_source 中的行不匹配的行,則當 condition 缺失或其計算結果為 true 時,將執行 WHEN 子句。

如果 WHEN 子句指定 WHEN NOT MATCHED [BY TARGET],並且候選更改行代表 data_source 中與目標表中的行不匹配的行,則當 condition 缺失或其計算結果為 true 時,將執行 WHEN 子句。

condition

一個返回 boolean 型別值的表示式。如果某個 WHEN 子句的該表示式返回 true,則為該行執行該子句的操作。

WHEN MATCHED 子句中的條件可以引用源關係和目標關係中的列。WHEN NOT MATCHED BY SOURCE 子句中的條件只能引用目標關係中的列,因為根據定義沒有匹配的源行。WHEN NOT MATCHED [BY TARGET] 子句中的條件只能引用源關係中的列,因為根據定義沒有匹配的目標行。只有目標表的系統屬性是可訪問的。

merge_insert

指定一個 INSERT 操作,將一行插入到目標表中。目標列名可以按任何順序排列。如果根本沒有給出列名列表,則預設為表的所有列,按宣告的順序排列。

不在顯式或隱式列列表中的每個列都將填充預設值,如果是其宣告的預設值,或者如果沒有任何預設值則為 null。

如果目標表是分割槽表,則每行將路由到適當的分割槽並插入其中。如果目標表是分割槽,則如果任何輸入行違反分割槽約束,將發生錯誤。

列名不能重複指定。INSERT 操作不能包含子查詢。

只能指定一個 VALUES 子句。VALUES 子句只能引用源關係中的列,因為根據定義沒有匹配的目標行。

merge_update

指定一個 UPDATE 操作,該操作更新目標表的當前行。列名不能重複指定。

不允許表名或 WHERE 子句。

merge_delete

指定一個 DELETE 操作,該操作刪除目標表的當前行。不要包含表名或任何其他子句,就像您通常對 DELETE 命令所做的那樣。

column_name

目標表中的列名。如果需要,列名可以由子欄位名或陣列下標限定。(僅將值插入複合列的某些欄位會將其他欄位留空。)請勿在目標列的規範中包含表的名稱。

OVERRIDING SYSTEM VALUE

沒有此子句,為定義為 GENERATED ALWAYS 的標識列指定顯式值(DEFAULT 除外)將導致錯誤。此子句覆蓋了該限制。

OVERRIDING USER VALUE

如果指定了此子句,則將忽略為定義為 GENERATED BY DEFAULT 的標識列提供的任何值,並將應用預設的序列生成值。

DEFAULT VALUES

所有列都將用其預設值填充。(此形式不允許使用 OVERRIDING 子句。)

expression

分配給列的表示式。如果在 WHEN MATCHED 子句中使用,該表示式可以使用目標表中原始行的值和 data_source 行的值。如果在 WHEN NOT MATCHED BY SOURCE 子句中使用,該表示式只能使用目標表中原始行的值。如果在 WHEN NOT MATCHED [BY TARGET] 子句中使用,該表示式只能使用 data_source 行的值。

DEFAULT

將列設定為其預設值(如果未為其分配特定的預設表示式,則為 NULL)。

sub-SELECT

一個 SELECT 子查詢,它產生與前面括號中的列列表一樣多的輸出列。子查詢執行時必須只產生一行。如果產生一行,則其列值將被分配給目標列;如果未產生行,則將 NULL 值分配給目標列。如果在 WHEN MATCHED 子句中使用,子查詢可以引用目標表中原始行的值和 data_source 行的值。如果在 WHEN NOT MATCHED BY SOURCE 子句中使用,子查詢只能引用目標表中原始行的值。

output_alias

RETURNING 列表中的 OLDNEW 行的可選替代名稱。

預設情況下,可以透過編寫 OLD.column_nameOLD.* 返回目標表中的舊值,透過編寫 NEW.column_nameNEW.* 返回新值。提供別名時,將隱藏這些名稱,並且必須使用別名引用舊行或新行。例如 RETURNING WITH (OLD AS o, NEW AS n) o.*, n.*

output_expression

一個在每行被更改(無論是否插入、更新或刪除)後由 MERGE 命令計算並返回的表示式。該表示式可以使用源表或目標表的任何列,或者使用 merge_action() 函式返回有關已執行操作的附加資訊。

編寫 * 將返回源表的所有列,然後是目標表的所有列。這通常會導致大量重複,因為源表和目標表經常有很多相同的列。這可以透過用源表或目標表的名稱或別名限定 * 來避免。

也可以使用 OLDNEW,或者 OLDNEW 的相應 output_alias 來限定列名或 *,以便返回目標表的舊值或新值。來自目標表的未限定列名,或者使用目標表名或別名限定的列名或 *,將為 INSERTUPDATE 操作返回新值,為 DELETE 操作返回舊值。

output_name

用於返回列的名稱。

輸出

成功完成時,MERGE 命令返回一個命令標籤,格式為

MERGE total_count

total_count 是更改的總行數(無論是插入、更新還是刪除)。如果 total_count 為 0,則沒有行以任何方式被更改。

如果 MERGE 命令包含 RETURNING 子句,則結果將類似於包含 RETURNING 列表中定義的列和值的 SELECT 語句,該語句在命令插入、更新或刪除的行上進行計算。

註釋

MERGE 執行期間,將按以下步驟進行。

  1. 為指定的所有操作執行任何 BEFORE STATEMENT 觸發器,無論其 WHEN 子句是否匹配。

  2. 從源表連線到目標表。生成的查詢將正常最佳化,並將產生一組候選更改行。對於每個候選更改行,

    1. 評估每行是 MATCHEDNOT MATCHED BY SOURCE 還是 NOT MATCHED [BY TARGET]

    2. 按指定的順序測試每個 WHEN 條件,直到有一個返回 true。

    3. 當條件返回 true 時,執行以下操作:

      1. 執行針對操作事件型別觸發的任何 BEFORE ROW 觸發器。

      2. 執行指定的操作,呼叫目標表上的任何檢查約束。

      3. 執行針對操作事件型別觸發的任何 AFTER ROW 觸發器。

      如果目標關係是一個帶有針對操作事件型別的 INSTEAD OF ROW 觸發器的檢視,則它們用於代替執行操作。

  3. 為指定的操作執行任何 AFTER STATEMENT 觸發器,無論它們是否實際發生。這類似於修改了零行的 UPDATE 語句的行為。

總之,對於某個事件型別(例如 INSERT)的語句觸發器,將在我們 指定 該型別操作時觸發。相比之下,行級觸發器僅在 執行 特定事件型別時觸發。因此,MERGE 命令可能會觸發 UPDATEINSERT 的語句觸發器,即使只觸發了 UPDATE 的行觸發器。

您應該確保連線最多為每個目標行產生一個候選更改行。換句話說,目標行不應該連線到多個數據源行。如果連線了,那麼只有其中一個候選更改行將用於修改目標行;後續修改該行的嘗試將導致錯誤。如果行觸發器修改了目標表,並且這些被修改的行隨後也被 MERGE 修改,也可能發生這種情況。如果重複的操作是 INSERT,這將導致唯一性衝突,而重複的 UPDATEDELETE 將導致基數衝突;後者行為是SQL標準要求的。這與 PostgreSQLUPDATEDELETE 語句中連線的歷史行為不同,在歷史行為中,第二次及後續修改同一行的嘗試將被簡單地忽略。

如果 WHEN 子句省略了 AND 子句,它將成為該型別(MATCHEDNOT MATCHED BY SOURCENOT MATCHED [BY TARGET])的最後一個可達子句。如果指定了該型別的後續 WHEN 子句,它將是可證明不可達的,並引發錯誤。如果兩種型別的可達子句都沒有指定,則可能不會對候選更改行執行任何操作。

資料來源生成的行的順序預設是不確定的。可以使用 source_query 來指定一致的順序(如果需要),這可能需要避免併發事務之間的死鎖。

MERGE 與其他修改目標表的命令併發執行時,將應用常規的事務隔離規則;有關每個隔離級別的行為解釋,請參閱 第 13.2 節。您可能還希望考慮使用 INSERT ... ON CONFLICT 作為替代語句,該語句提供了在發生併發 INSERT 時執行 UPDATE 的能力。這兩種語句型別之間存在各種差異和限制,它們不能互換使用。

示例

根據新的 recent_transactions,對 customer_accounts 執行維護。

MERGE INTO customer_account ca
USING recent_transactions t
ON t.customer_id = ca.customer_id
WHEN MATCHED THEN
  UPDATE SET balance = balance + transaction_value
WHEN NOT MATCHED THEN
  INSERT (customer_id, balance)
  VALUES (t.customer_id, t.transaction_value);

嘗試插入新庫存商品以及庫存數量。如果商品已存在,則更新現有商品的庫存數量。不允許零庫存的錄入。返回所有更改的詳細資訊。

MERGE INTO wines w
USING wine_stock_changes s
ON s.winename = w.winename
WHEN NOT MATCHED AND s.stock_delta > 0 THEN
  INSERT VALUES(s.winename, s.stock_delta)
WHEN MATCHED AND w.stock + s.stock_delta > 0 THEN
  UPDATE SET stock = w.stock + s.stock_delta
WHEN MATCHED THEN
  DELETE
RETURNING merge_action(), w.winename, old.stock AS old_stock, new.stock AS new_stock;

wine_stock_changes 表可能例如是一個最近載入到資料庫中的臨時表。

根據新的葡萄酒列表更新 wines,為任何新庫存插入行,更新修改過的庫存條目,並刪除新列表中不存在的任何葡萄酒。

MERGE INTO wines w
USING new_wine_list s
ON s.winename = w.winename
WHEN NOT MATCHED BY TARGET THEN
  INSERT VALUES(s.winename, s.stock)
WHEN MATCHED AND w.stock != s.stock THEN
  UPDATE SET stock = s.stock
WHEN NOT MATCHED BY SOURCE THEN
  DELETE;

相容性

此命令符合SQLSQL 標準的一部分。

WITH 子句,WHEN NOT MATCHEDBY SOURCEBY TARGET 限定符,DO NOTHING 操作,以及 RETURNING 子句是對SQLSQL 標準的一部分。

提交更正

如果您在文件中看到任何不正確、與您對特定功能的經驗不符或需要進一步澄清的內容,請使用 此表單 來報告文件問題。