MERGE — 條件性地插入、更新或刪除表中的行
[ WITHwith_query
[, ...] ] MERGE INTO [ ONLY ]target_table_name
[ * ] [ [ AS ]target_alias
] USINGdata_source
ONjoin_condition
when_clause
[...] [ RETURNING [ WITH ( { OLD | NEW } ASoutput_alias
[, ...] ) ] { * |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
對目標表 target_table_name
中標識的行執行修改操作,使用 data_source
。 MERGE
提供了一個單一的SQL語句,可以有條件地 INSERT
、UPDATE
或 DELETE
行,而這項任務否則需要多個過程語言語句。
首先,MERGE
命令從 data_source
連線到目標表,產生零個或多個候選更改行。對於每個候選更改行,MATCHED
、NOT MATCHED BY SOURCE
或 NOT MATCHED [BY TARGET]
的狀態僅設定一次,之後 WHEN
子句按指定的順序進行評估。對於每個候選更改行,第一個求值為真的子句將被執行。對於任何候選更改行,最多執行一個 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。請注意,MERGE
不支援 WITH RECURSIVE
。
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
中的哪些行與目標表中的行匹配。
僅應在 join_condition
中出現嘗試與 data_source
行匹配的目標表列。僅引用目標表列的 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標準的擴充套件,與 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
列表中的 OLD
或 NEW
行的可選替代名稱。
預設情況下,可以透過編寫 OLD.
或 column_name
OLD.*
返回目標表中的舊值,透過編寫 NEW.
或 column_name
NEW.*
返回新值。提供別名時,將隱藏這些名稱,並且必須使用別名引用舊行或新行。例如 RETURNING WITH (OLD AS o, NEW AS n) o.*, n.*
。
output_expression
一個在每行被更改(無論是否插入、更新或刪除)後由 MERGE
命令計算並返回的表示式。該表示式可以使用源表或目標表的任何列,或者使用 merge_action()
函式返回有關已執行操作的附加資訊。
編寫 *
將返回源表的所有列,然後是目標表的所有列。這通常會導致大量重複,因為源表和目標表經常有很多相同的列。這可以透過用源表或目標表的名稱或別名限定 *
來避免。
也可以使用 OLD
或 NEW
,或者 OLD
或 NEW
的相應 output_alias
來限定列名或 *
,以便返回目標表的舊值或新值。來自目標表的未限定列名,或者使用目標表名或別名限定的列名或 *
,將為 INSERT
和 UPDATE
操作返回新值,為 DELETE
操作返回舊值。
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
)的語句觸發器,將在我們 指定 該型別操作時觸發。相比之下,行級觸發器僅在 執行 特定事件型別時觸發。因此,MERGE
命令可能會觸發 UPDATE
和 INSERT
的語句觸發器,即使只觸發了 UPDATE
的行觸發器。
您應該確保連線最多為每個目標行產生一個候選更改行。換句話說,目標行不應該連線到多個數據源行。如果連線了,那麼只有其中一個候選更改行將用於修改目標行;後續修改該行的嘗試將導致錯誤。如果行觸發器修改了目標表,並且這些被修改的行隨後也被 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);
嘗試插入新庫存商品以及庫存數量。如果商品已存在,則更新現有商品的庫存數量。不允許零庫存的錄入。返回所有更改的詳細資訊。
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 MATCHED
的 BY SOURCE
和 BY TARGET
限定符,DO NOTHING
操作,以及 RETURNING
子句是對SQLSQL
標準的一部分。
如果您在文件中看到任何不正確、與您對特定功能的經驗不符或需要進一步澄清的內容,請使用 此表單 來報告文件問題。