MERGE — 有條件地插入、更新或刪除表格中的列
[ WITHwith_query
[, ...] ] MERGE INTO [ ONLY ]target_table_name
[ * ] [ [ AS ]target_alias
] USINGdata_source
ONjoin_condition
when_clause
[...] [ RETURNING { * |output_expression
[ [ AS ]output_name
] } [, ...] ] wheredata_source
is: { [ ONLY ]source_table_name
[ * ] | (source_query
) } [ [ AS ]source_alias
] andwhen_clause
is: { WHEN MATCHED [ ANDcondition
] THEN {merge_update
|merge_delete
| DO NOTHING } | WHEN NOT MATCHED BY SOURCE [ ANDcondition
] THEN {merge_update
|merge_delete
| DO NOTHING } | WHEN NOT MATCHED [ BY TARGET ] [ ANDcondition
] THEN {merge_insert
| DO NOTHING } } andmerge_insert
is: INSERT [(column_name
[, ...] )] [ OVERRIDING { SYSTEM | USER } VALUE ] { VALUES ( {expression
| DEFAULT } [, ...] ) | DEFAULT VALUES } andmerge_update
is: UPDATE SET {column_name
= {expression
| DEFAULT } | (column_name
[, ...] ) = [ ROW ] ( {expression
| DEFAULT } [, ...] ) | (column_name
[, ...] ) = (sub-SELECT
) } [, ...] andmerge_delete
is: DELETE
MERGE
執行動作,以使用 data_source
修改識別為 target_table_name
的目標表格中的列。MERGE
提供單一SQL陳述式,可以有條件地 INSERT
、UPDATE
或 DELETE
列,這項任務在其他情況下需要多個程序語言陳述式。
首先,MERGE
命令執行從 data_source
到目標表格的聯結,以產生零個或多個候選變更列。對於每個候選變更列,MATCHED
、NOT MATCHED BY SOURCE
或 NOT MATCHED [BY TARGET]
的狀態只會設定一次,之後會按照指定的順序評估 WHEN
子句。對於每個候選變更列,會執行評估結果為 true 的第一個子句。對於任何候選變更列,最多執行一個 WHEN
子句。
MERGE
動作具有與同名的常規 UPDATE
、INSERT
或 DELETE
命令相同的效果。這些命令的語法不同,特別是沒有 WHERE
子句,也沒有指定表格名稱。所有動作都參考目標表格,但可以使用觸發程序對其他表格進行修改。
當指定 DO NOTHING
時,會跳過來源列。由於動作按照其指定的順序進行評估,因此在進行更精細的處理之前,DO NOTHING
可以方便地跳過不感興趣的來源列。
可選的 RETURNING
子句會導致 MERGE
根據每個插入、更新或刪除的列來計算並傳回值。可以使用來源或目標表格的欄,或 merge_action()
函數的任何運算式都可以計算。當執行 INSERT
或 UPDATE
動作時,會使用目標表格欄的新值。當執行 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
子句中指定的每種類型的動作 (INSERT
、UPDATE
和 DELETE
) 的 INSTEAD OF
觸發程序。不支援具有規則的檢視。
target_alias
目標表格的替代名稱。當提供別名時,它會完全隱藏表格的實際名稱。例如,給定 MERGE INTO foo AS f
,MERGE
陳述式的其餘部分必須將此表格稱為 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 SOURCE
和 WHEN NOT MATCHED [BY TARGET]
子句,MERGE
指令將會在 data_source
和目標表格之間執行 FULL
聯結。為使此操作生效,至少一個 join_condition
子表示式必須使用支援雜湊聯結的運算子,或者所有子表示式都必須使用支援合併聯結的運算子。
when_clause
至少需要一個 WHEN
子句。
WHEN
子句可以指定 WHEN MATCHED
、WHEN NOT MATCHED BY SOURCE
或 WHEN NOT MATCHED [BY TARGET]
。請注意,SQL標準僅定義 WHEN MATCHED
和 WHEN 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
期間會發生以下步驟。
對所有指定的操作執行任何 BEFORE STATEMENT
觸發器,無論其 WHEN
子句是否匹配。
執行從來源到目標資料表的聯結。產生的查詢將被正常最佳化,並產生一組候選變更列。對於每個候選變更列,
評估每一列是 MATCHED
、NOT MATCHED BY SOURCE
還是 NOT MATCHED [BY TARGET]
。
依照指定的順序測試每個 WHEN
條件,直到其中一個傳回 true。
當條件傳回 true 時,執行以下動作
執行任何觸發操作事件類型的 BEFORE ROW
觸發器。
執行指定的操作,對目標資料表調用任何檢查約束。
執行任何觸發操作事件類型的 AFTER ROW
觸發器。
如果目標關聯是一個具有操作事件類型 INSTEAD OF ROW
觸發器的視窗,則它們將用於執行該操作。
對指定的動作執行任何 AFTER STATEMENT
觸發器,無論它們是否實際發生。這種類似於未修改任何列的 UPDATE
陳述式的行為。
總之,事件類型(例如,INSERT
)的陳述式觸發器將在我們 指定 該類型的動作時觸發。 相反,列層級的觸發器僅會為 執行 的特定事件類型觸發。 因此,即使僅觸發了 UPDATE
列觸發器,MERGE
指令也可能會觸發 UPDATE
和 INSERT
的陳述式觸發器。
您應該確保聯結為每個目標列產生最多一個候選變更列。 換句話說,目標列不應聯結到多個資料來源列。 如果是這樣,則只會使用其中一個候選變更列來修改目標列; 後續嘗試修改該列將會導致錯誤。 如果列觸發器對目標資料表進行變更,並且隨後也由 MERGE
修改了這些變更的列,則也可能發生這種情況。 如果重複的動作是 INSERT
,這將導致唯一性違規,而重複的 UPDATE
或 DELETE
將導致基數違規; 後一種行為是SQL標準所要求的。 這與 PostgreSQL 在 UPDATE
和 DELETE
陳述式中聯結的歷史行為不同,在這些陳述式中,第二次和後續嘗試修改同一列的行為將被忽略。
如果 WHEN
子句省略了 AND
子子句,則它將成為該類型(MATCHED
、NOT MATCHED BY SOURCE
或 NOT 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);
請注意,這將與以下陳述式完全等效,因為 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 SOURCE
和 BY TARGET
限定詞(用於 WHEN NOT MATCHED
)、DO NOTHING
動作和 RETURNING
子句是對SQL標準。
如果您在文件中發現任何不正確、與特定功能的經驗不符或需要進一步澄清的地方,請使用 此表單 來回報文件問題。