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
权限。您还需要对任何 condition
(包括 join_condition
)或 expression
中引用的 data_source
和目标表的任何列具有 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
中的哪些行与目标表中的行匹配。
只有尝试匹配 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
子句,WHEN NOT MATCHED
的 BY SOURCE
和 BY TARGET
限定符,DO NOTHING
操作和 RETURNING
子句是对SQL标准。
如果您发现文档中的任何内容不正确,与您使用特定功能的体验不符或需要进一步澄清,请使用此表单来报告文档问题。