MERGE

MERGE — 基于源数据插入、更新表中的行

Synopsis

            MERGE INTO target_table_name [ [ AS ] target_alias ]
            USING data_source
            ON join_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
            }
            [ WHERE condition ]

            而 merge_update 是

            UPDATE SET { column_name = { 
                expression | DEFAULT } |
            ( column_name [, ...] ) = ( { 
                expression | DEFAULT } [, ...] )
            } [, ...]
            [ WHERE condition ]
            [ DELETE WHERE condition ]

        

描述

MERGE 命令通过使用 data_source,对 target_table_name 中的行进行修改。MERGE 提供了一个单一的 SQL 语句,可以有条件地 INSERTUPDATE 行,这是一个通常需要多个过程式语言语句才能完成的任务。

首先,MERGE 命令从 data_sourcetarget_table_name 执行联接,产生零个或多个候选更改行。 对于每个候选更改行,MATCHEDNOT MATCHED 的状态仅设置一次, 然后按指定顺序评估 WHEN 子句。如果其中一个被激活,则执行指定的操作。 对于任何候选更改行,最多只能激活一个 WHEN 子句。

MERGE 命令的操作与同名的常规 UPDATEINSERT 命令具有相同的效果。这些命令的语法不同,特别是没有指定表名。所有操作都参考 target_table_name

没有 MERGE 权限。如果指定了更新操作,则必须在 SET 子句中引用 target_table_name 的列的 UPDATE 权限, 如果指定了插入操作,则必须在 target_table_name 上拥有 INSERT 权限。权限在语句开始时进行测试,无论是否激活特定的 WHEN 子句,在随后的执行过程中都会进行检查。在引用 condition 中的 data_sourcetarget_table_name 的任何列时,都需要具有 SELECT 权限。

如果 target_table_name 上定义了 RULES,则不支持 MERGE。

注意

必须确保目标表中的一条记录不能与源表中的记录多次匹配,因为这可能导致目标表中更新的记录没有特定值。

参数

target_table_name

要合并到的目标表的名称(可选带模式限定符)。

target_alias

目标表的替代名称。提供别名时,它完全隐藏了表的实际名称。例如,给定 MERGE foo AS fMERGE 语句的其余部分必须将该表称为 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 中的行匹配。

Warning

只有尝试匹配 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')
                ;