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
执行修改目标表中行(标识为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
中哪些行与目标表中的行匹配。
只有试图匹配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标准的扩展,与向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
插入操作的规范,该操作将一行插入到目标表中。目标列名可以按任何顺序列出。如果根本没有给出列名列表,则默认为所有表的列,并按其声明的顺序排列。
显式或隐式列列表中不存在的每个列都将填充默认值,即其声明的默认值或(如果不存在)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
)的语句触发器将在我们指定该类型的操作时触发。相反,行级触发器仅对正在执行的特定事件类型触发。因此,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);
请注意,这将与以下语句完全等效,因为 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标准。
如果您在文档中看到任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用此表单报告文档问题。