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
标准的一部分。
如果您在文档中看到任何不正确、与您对特定功能的经验不符或需要进一步澄清的内容,请使用 此表单 来报告文档问题。