动机:
想在Oracle中用一条SQL语句直接进行Insert/Update的操作。
说明:
在进行SQL语句编写时,我们经常会遇到大量的同时进行Insert/Update的语句
,也就是说当存在记录时,就更新(Update),不存在数据时,就插入(Insert)。
merge into 是特有的功能,相当于在
MSSQL中的
if exists(...)
update table
else
Insert into
table.
merge into 语法不仅没有if exists语法啰嗦,而且比if exists
还要高效很多。我经常用来在oracle之间同步数据库表。
语法如下:
MERGE INTO table_name
alias1
USING (table|view|sub_query) alias2
ON (join
condition)
WHEN MATCHED THEN
UPDATE
table_name
SET col1 = col_val1,
col2 =
col2_val
WHEN NOT MATCHED THEN
INSERT (column_list) VALUES
(column_values);
如果不懂Merge语句的原理,Merge语句是一条比较危险的语句,特别是在您只想更新一条记录的时候,因为不经意间,你可能就把整表的数据都Update了一遍.....汗!!!
ORACLE
9I中加入了MERGE
语法:
MERGE [hint] INTO [schema .] table
[t_alias]
USING [schema .] { table | view | subquery }
[t_alias]
ON ( condition )
WHEN MATCHED THEN
merge_update_clause
WHEN NOT MATCHED THEN
merge_insert_clause;
创建测试数据表:
create table tj_test(id number,name
varchar2(20),age number);
向表中插入数据:
insert into tj_test values
(1,‘jan‘,23);
insert into tj_test values (2,‘kk‘,22);
insert into
tj_test values (3,‘joe‘,27);
select * from
tj_test;
查询结果如下:
1 jan 23
2 kk 22
3 joe
27
创建另一新表
create table tj_test1 as select * from tj_test where
1=0
插入一条数据
insert into tj_test1 values (1,‘jlk‘,23);
select
* from tj_test1
查询结果如下:
1 jkl 23
--注意,这里的的NAME字段中的值是jkl
使用MERGE,实现有则更新,无则插入,sql语句如下:
merge into
tj_test1 tt1
using tj_test tt
on (tt1.id=tt.id)
when
matched then
update
set
tt1.name=tt.name,
tt1.age=tt.age
when not matched
then
insert
values(
tt.id,
tt.name,
tt.age)
查询tj_test1表(对比原来表中的数据,更新了ID=1
ROW中字段NAME,同时多出两条新数据)
select * from tj_test1
改变行数据如下:
1 jan
23 --这里的原有jkl值被更新
3 joe 27 --原来表中没有的插入
2 kk 22
--原来表中没有的插入
如果存在就更新,不存在就插入
9i已经支持了,是Merge,但是只支持select子查询,
如果是单条数据记录,可以写作select
…… from dual的子查询。
语法为:
MERGE INTO table
USING
data_source
ON (condition)
WHEN MATCHED THEN
update_clause
WHEN NOT MATCHED THEN insert_clause;
如:
MERGE
INTO course c
USING (SELECT course_name,
period,
course_hours
FROM course_updates) cu
ON
(c.course_name = cu.course_name
AND c.period = cu.period)
WHEN
MATCHED THEN
UPDATE
SET c.course_hours =
cu.course_hours
WHEN NOT MATCHED THEN
INSERT (c.course_name,
c.period,
c.course_hours)
VALUES (cu.course_name,
cu.period,
cu.course_hours);
/*
Merge into 主要用作数据同步,把一张表中的数据同步到另一张表,可以用这个函数
*/
Merge是一个非常有用的功能,类似于Mysql里的insert into on duplicate
key.
Oracle在9i引入了merge命令,
通过这个merge你能够在一个SQL语句中对一个表同时执行inserts和updates操作.
当然是update还是insert是依据于你的指定的条件判断的,Merge into可以实现用B表来更新A表数据,如果A表中没有,则把B表的数据插入A表.
MERGE命令从一个或多个数据源中选择行来updating或inserting到一个或多个表
语法如下
MERGE
INTO [your table-name] [rename your table here]
USING ( [write your
query here] )[rename your query-sql and using just like a table]
ON
([conditional expression here] AND [...]...)
WHEN MATHED THEN [here you
can execute some update sql or something else ]
WHEN NOT MATHED THEN
[execute something else here ! ]
我们先看看一个简单的例子,来介绍一个merge
into的用法
merge into products p using newproducts np on (p.product_id =
np.product_id)
when matched then
update set p.product_name =
np.product_name
when not matched then
insert
values(np.product_id, np.product_name,
np.category)
在这个例子里。前面的merger into products using newproducts
表示的用newproducts表来merge到products表,merge的匹配关系就是on后面的条件子句的内容,这里根据两个表的product_id来进行匹配,那么匹配上了我们的操作是就是when
matched then的子句里的动作了,这里的动作是update set p.product_name = np.product_name,
很显然就是把newproduct里的内容,赋值到product的product_name里。如果没有匹配上则insert这样的一条语句进去。
大家看看这个merget
inot的用法是不是一目了然了呀。这里merger的功能,好比比较,然后选择更新或者是插入,是一系列的组合拳,在做merge的时候,这样同样的情况下,merge的性能是优于同等功能的update/insert语句的。有人曾经分析merge是批量处理对性能贡献很大,个人觉得这个是没有考据的。
我们也可以在using后面使用视图或者子查询。比如我们把newproducts换成
merge
into products p using (select * from newproducts) np on (p.product_id =
np.product_id)
when matched then
update set p.product_name =
np.product_name
when not matched then
insert
values(np.product_id, np.product_name,
np.category)
也是可以的。
在Oracle
10g中MERGE有如下一些改进:
1、UPDATE或INSERT子句是可选的
2、UPDATE和INSERT子句可以加WHERE子句
3、在ON条件中使用常量过滤谓词来insert所有的行到目标表中,不需要连接源表和目标表
4、UPDATE子句后面可以跟DELETE子句来去除一些不需要的行
我们通过实例来一一看看如上的新特性
1.
UPDATE或INSERT子句是可选的
在9i里由于必须insert
into和update都要存在,也就是不是update就是insert,不支持单一的操作,虽然还是可以曲线救国,呵呵
但是有些过于强势了。而10g里就是可选了,能符合我们更多的需求了
比如上面的句子
我们可以只存在update或者insert
merge
into products p using newproducts np on (p.product_id =
np.product_id)
when matched then
update set p.product_name =
np.product_name
这里,如果匹配就更新,不存在就不管了。
2.
UPDATE和INSERT子句可以加WHERE子句
这也是一个功能性的改进,能够符合我们更多的需求,这个where的作用很明显是一个过滤的条件,是我们加入一些额外的条件,对只对满足where条件的进行更新和insert
merge
into products p using (select * from newproducts) np on (p.product_id =
np.product_id)
when matched then
update set p.product_name =
np.product_name where np.product_name like
‘OL%‘
这里表示只是对product_name开头是‘OL‘的匹配上的进行update,如果开头不是‘OL‘的就是匹配了也不做什么事情,insert里也可以加入where
比如
merge
into products p using (select * from newproducts) np on (p.product_id =
np.product_id)
when matched then
update set p.product_name =
np.product_name where np.product_name like ‘OL%‘
when not matched
then
insert values(np.product_id, np.product_name, np.category) where
np.product_name like
‘OL%‘
这里注意比较一下,他们返回的结果行数,是有着差异的。
3.
在ON条件中使用常量过滤谓词来insert所有的行到目标表中,不需要连接源表和目标表
merge into products p
using (select * from newproducts) np on (1=0)
when matched
then
update set p.product_name = np.product_name
when not
matched then
insert values(np.product_id, np.product_name,
np.category)
个人觉得这个功能没有太大的意义,我们的insert
into本身就支持这样的功能,没有必要使用merge
4.
UPDATE子句后面可以跟DELETE子句来去除一些不需要的行
delete只能和update配合,从而达到删除满足where条件的子句的纪录
merge
into products p using (select * from newproducts) np on (p.product_id =
np.product_id)
when matched then
update set p.product_name =
np.product_name delete where p.product_id = np.product_id where np.product_name
like ‘OL%‘
when not matched then
insert values(np.product_id,
np.product_name, np.category)
这里我们达到的目的就是
会把匹配的记录的prodcut_name更新到product里,并且把product_name开头为OL的删除掉。
merge
into也是一个dml语句,和其他的dml语句一样需要通过rollback和commit
结束事务。
Merge是一个非常强大的功能,而且是我们需求里经常会用到的一个有用的功能,所以我们一定要好好的学习到。
文中需要的测试脚本在附件里提供下载。
merge
into sample.sql