MERGE — 基于源数据插入、更新表中的行
MERGE INTOtarget_table_name
[ [ AS ]target_alias
] USINGdata_source
ONjoin_condition
when_clause
[...] 其中data_source
是 {source_table_name
| ( source_query ) } [ [ AS ]source_alias
] 而when_clause
是 { WHEN MATCHED THEN {merge_update
} | WHEN NOT MATCHED THEN {merge_insert
} } 而merge_insert
是 INSERT [(column_name
[, ...] )] { VALUES ( {expression
| DEFAULT } [, ...] ) | DEFAULT VALUES } [ WHEREcondition
] 而merge_update
是 UPDATE SET {column_name
= {expression
| DEFAULT } | (column_name
[, ...] ) = ( {expression
| DEFAULT } [, ...] ) } [, ...] [ WHEREcondition
] [ DELETE WHEREcondition
]
MERGE
命令通过使用 data_source
,对
target_table_name
中的行进行修改。MERGE
提供了一个单一的 SQL 语句,可以有条件地 INSERT
或
UPDATE
行,这是一个通常需要多个过程式语言语句才能完成的任务。
首先,MERGE
命令从 data_source
到
target_table_name
执行联接,产生零个或多个候选更改行。
对于每个候选更改行,MATCHED
或 NOT MATCHED
的状态仅设置一次,
然后按指定顺序评估 WHEN
子句。如果其中一个被激活,则执行指定的操作。
对于任何候选更改行,最多只能激活一个 WHEN
子句。
MERGE
命令的操作与同名的常规 UPDATE
、INSERT
命令具有相同的效果。这些命令的语法不同,特别是没有指定表名。所有操作都参考
target_table_name
。
没有 MERGE 权限。如果指定了更新操作,则必须在 SET
子句中引用
target_table_name
的列的 UPDATE
权限,
如果指定了插入操作,则必须在 target_table_name
上拥有
INSERT
权限。权限在语句开始时进行测试,无论是否激活特定的 WHEN
子句,在随后的执行过程中都会进行检查。在引用 condition
中的
data_source
和 target_table_name
的任何列时,都需要具有 SELECT
权限。
如果 target_table_name
上定义了 RULES
,则不支持 MERGE。
必须确保目标表中的一条记录不能与源表中的记录多次匹配,因为这可能导致目标表中更新的记录没有特定值。
target_table_name
要合并到的目标表的名称(可选带模式限定符)。
target_alias
目标表的替代名称。提供别名时,它完全隐藏了表的实际名称。例如,给定
MERGE foo AS f
,MERGE
语句的其余部分必须将该表称为
f
而不是 foo
。
source_table_name
源表、视图或转换表的名称(可选带模式限定符)。
source_query
一个查询(SELECT
语句或 VALUES
语句),提供要合并到
target_table_name
中的行。请参考
SELECT
语句或
VALUES
语句,了解其语法的说明。
source_alias
数据源的替代名称。提供别名时,它完全隐藏了指定了表还是查询。
join_condition
join_condition
是一个表达式,其结果为
boolean
类型的值(类似于 WHERE
子句),指定哪些
data_source
中的行与
target_table_name
中的行匹配。
只有尝试匹配 data_source
行的
target_table_name
列应出现在
join_condition
中。仅引用
target_table_name
列的
join_condition
子表达式只能影响采取哪些操作,
通常是以令人惊讶的方式。
when_clause
至少需要一个 WHEN
子句。
如果 WHEN
子句指定了 WHEN MATCHED
,并且候选更改行与
target_table_name
中的一行匹配,
如果 condition
不存在或存在且评估为
true
,则会激活 WHEN
子句。
如果 WHEN
子句指定了 WHEN NOT MATCHED
,并且候选更改行与
target_table_name
中的一行不匹配,
如果 condition
不存在或存在且评估为
true
,则会激活 WHEN
子句。
merge_insert
指定一个 INSERT
操作,将一行插入目标表中。
目标列名可以以任何顺序列出。如果根本没有给出列名列表,则默认为表中所有列按其声明的顺序。
未在显式或隐式列列表中出现的每个列将填充其声明的默认值或 null(如果没有默认值)。
如果任何列的表达式不是正确的数据类型,则会尝试进行自动类型转换。
如果 target_table_name
是分区表,则每行将路由到适当的分区并插入其中。
如果 target_table_name
是分区,则如果其中一个输入行违反分区约束,则会出现错误。
列名不能指定多次。 INSERT
操作不能包含子查询。
只能指定一个 VALUES
子句。
VALUES
子句只能引用来自源关系的列,因为根据定义,没有匹配的目标行。
merge_update
指定一个 UPDATE
操作,更新 target_table_name
的当前行。列名不能指定多次。
不要包含表名,因为这与 UPDATE 命令通常不同。
例如,UPDATE tab SET col = 1
是无效的。
自版本24.1.3之后,可以在UPDATE
子句后面添加一个DELETE
子句,DELETE
子句语法如下:
DELETE
WHERE条件
条件
是一个用于过滤删除行的表达式。
请注意,如果设置了DELETE
子句,target_table_name
被限制为只支持表类型 (例如, 视图不被支持), 而且只有此事务中匹配到的行受DELETE
子句影响。
DELETE
只在oracle兼容模式下支持,如果目标表在merge into操作之前,有元组已经更新(包括删除,插入),此merge into操作不被允许。
column_name
target_table_name
中的列名。
如果需要,可以使用子字段名或数组下标限定列名。(仅插入复合列的某些字段将使其他字段为空。)
在引用列时,不要在目标列的规范中包含表名。
expression
赋给该列的表达式。该表达式可以使用该表中此列和其他列的旧值。
condition
返回类型为 boolean
的表达式。如果此表达式返回 true
,则将激活 WHEN
子句,
并对该行执行相应的操作。该表达式可能不包含可能对数据库执行写操作的函数。
对于 WHEN MATCHED
子句的条件可以引用源关系和目标关系中的列。
对于 WHEN NOT MATCHED
子句的条件只能引用来自源关系的列,
因为根据定义,没有匹配的目标行。
成功完成后,MERGE
命令将返回一个命令标记,格式如下:
MERGE total-count
total-count
是更改的总行数(无论是插入、更新还是删除)。
如果 total-count
为 0,则没有任何行发生任何更改。
如果有 DELETE
子句, 命令标记为 DELETE , total-count
为总删除的行数。
基于新事务对 CustomerAccounts 进行维护。
MERGE INTO CustomerAccount CA USING RecentTransactions T ON T.CustomerId = CA.CustomerId WHEN MATCHED THEN UPDATE SET Balance = Balance + TransactionValue WHEN NOT MATCHED THEN INSERT (CustomerId, Balance) VALUES (T.CustomerId, T.TransactionValue);
注意,由于执行过程中 MATCHED
结果不会改变,因此这与以下语句完全等效:
MERGE INTO CustomerAccount CA USING (Select CustomerId, TransactionValue From RecentTransactions) AS T ON CA.CustomerId = T.CustomerId WHEN NOT MATCHED THEN INSERT (CustomerId, Balance) VALUES (T.CustomerId, T.TransactionValue) WHEN MATCHED THEN UPDATE SET Balance = Balance + TransactionValue;
尝试插入一个新的库存项以及库存数量。如果该项已经存在,则更新现有项的库存计数。不允许具有零库存的条目。
MERGE INTO wines w USING wine_stock_changes s ON s.winename = w.winename WHEN NOT MATCHED THEN INSERT VALUES(s.winename, s.stock_delta) WHERE s.stock_delta > 0 WHEN MATCHED THEN UPDATE SET stock = w.stock + s.stock_delta WHERE w.stock + s.stock_delta > 0;
wine_stock_changes 表可以是最近加载到数据库中的临时表,例如。
尝试通过 DELETE
子句删除行:
create table tl( a char(10), b char(10), c char(10),d int, e char(10), f char(10) ); insert into tl(a,b,c,d,e,f) values('tl','b1','c1',1,'e1','f1_tl'); insert into tl(a,b,c,d,e,f) values('tl','b2','c2',2,'e2','f2_tl'); insert into tl(a,b,c,d,e,f) values('tl','b3','c3',3,'e3','f3_tl'); insert into tl(a,b,c,d,e,f) values('tl','b4','c4',4,'e4','f4_tl'); create table tr( a char(10), b char(10), c char(10),d int, e char(10), f char(10) ); insert into tr(a,b,c,d,e,f) values('tr','b1','c1',1,'e1','f1_tr'); insert into tr(a,b,c,d,e,f) values('tr','b2','c2',2,'e2','f2_tr'); insert into tr(a,b,c,d,e,f) values('tr','b33','c3',4,'e4','f3_tr'); merge into tl using tr on (tl.b=tr.b) when matched then update set tl.d=tl.d+1 delete where tr.b='b1' when not matched then insert values('tl_i','b3','c3',4,'e4_i','f1_i') ;