支援的版本:目前 (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 { * | 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 執行動作,以使用 data_source 修改識別為 target_table_name 的目標表格中的列。MERGE 提供單一SQL陳述式,可以有條件地 INSERTUPDATEDELETE 列,這項任務在其他情況下需要多個程序語言陳述式。

首先,MERGE 命令執行從 data_source 到目標表格的聯結,以產生零個或多個候選變更列。對於每個候選變更列,MATCHEDNOT MATCHED BY SOURCENOT MATCHED [BY TARGET] 的狀態只會設定一次,之後會按照指定的順序評估 WHEN 子句。對於每個候選變更列,會執行評估結果為 true 的第一個子句。對於任何候選變更列,最多執行一個 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。請注意,WITH RECURSIVE 不受 MERGE 支援。

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 中的哪些列與目標表格中的列相符。

警告

只有嘗試比對 data_source 列的目標表格中的欄才應出現在 join_condition 中。僅參考目標表格欄的 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而將 BY TARGET 附加到 WHEN NOT MATCHED 也是一個擴充,目的是使其含義更明確。

如果 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

目標表格中欄位的名稱。如果需要,欄位名稱可以用子欄位名稱或陣列下標來限定。(僅插入複合欄位的某些欄位會使其他欄位保持 null。)不要在目標欄位的規格中包含表格的名稱。

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_expression

一個表示式,用於在每個列變更(無論是插入、更新或刪除)後由 MERGE 指令計算和傳回。該表示式可以使用來源表格或目標表格的任何欄位,或者使用 merge_action() 函式來傳回有關已執行動作的更多資訊。

寫入 * 將會回傳來源資料表的所有欄位,接著是目標資料表的所有欄位。通常這會導致大量的重複資料,因為來源和目標資料表通常有很多相同的欄位。可以透過使用來源或目標資料表的名稱或別名來限定 * 的範圍,以避免這種情況。

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)的陳述式觸發器將在我們 指定 該類型的動作時觸發。 相反,列層級的觸發器僅會為 執行 的特定事件類型觸發。 因此,即使僅觸發了 UPDATE 列觸發器,MERGE 指令也可能會觸發 UPDATEINSERT 的陳述式觸發器。

您應該確保聯結為每個目標列產生最多一個候選變更列。 換句話說,目標列不應聯結到多個資料來源列。 如果是這樣,則只會使用其中一個候選變更列來修改目標列; 後續嘗試修改該列將會導致錯誤。 如果列觸發器對目標資料表進行變更,並且隨後也由 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_transactionscustomer_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);

請注意,這將與以下陳述式完全等效,因為 MATCHED 結果在執行期間不會變更。

MERGE INTO customer_account ca
USING (SELECT customer_id, transaction_value FROM recent_transactions) AS 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.*;

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;

相容性

此指令符合SQL標準。

WITH 子句、BY SOURCEBY TARGET 限定詞(用於 WHEN NOT MATCHED)、DO NOTHING 動作和 RETURNING 子句是對SQL標準。

提交更正

如果您在文件中發現任何不正確、與特定功能的經驗不符或需要進一步澄清的地方,請使用 此表單 來回報文件問題。