MERGE — 条件性地插入、更新或删除表中的行
[ WITHwith_query[, ...] ] MERGE INTO [ ONLY ]target_table_name[ * ] [ [ AS ]target_alias] USINGdata_sourceONjoin_conditionwhen_clause[...] [ RETURNING [ WITH ( { OLD | NEW } ASoutput_alias[, ...] ) ] { * |output_expression[ [ AS ]output_name] } [, ...] ] wheredata_sourceis: { [ ONLY ]source_table_name[ * ] | (source_query) } [ [ AS ]source_alias] andwhen_clauseis: { 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_insertis: INSERT [(column_name[, ...] )] [ OVERRIDING { SYSTEM | USER } VALUE ] { VALUES ( {expression| DEFAULT } [, ...] ) | DEFAULT VALUES } andmerge_updateis: UPDATE SET {column_name= {expression| DEFAULT } | (column_name[, ...] ) = [ ROW ] ( {expression| DEFAULT } [, ...] ) | (column_name[, ...] ) = (sub-SELECT) } [, ...] andmerge_deleteis: 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_queryWITH 子句允许您指定一个或多个可以在 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_conditionjoin_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_aliasRETURNING 列表中的 OLD 或 NEW 行的可选替代名称。
默认情况下,可以通过编写 OLD. 或 column_nameOLD.* 返回目标表中的旧值,通过编写 NEW. 或 column_nameNEW.* 返回新值。提供别名时,将隐藏这些名称,并且必须使用别名引用旧行或新行。例如 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 标准的一部分。