Oracle开发者中级第8课(Merge)实验
Posted dingdingfish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle开发者中级第8课(Merge)实验相关的知识,希望对你有一定的参考价值。
概述
本实验参考DevGym中的实验指南。
创建环境
create table bricks_for_sale (
colour varchar2(10),
shape varchar2(10),
price number(10, 2),
primary key ( colour, shape )
);
create table purchased_bricks (
colour varchar2(10),
shape varchar2(10),
price number(10, 2),
primary key ( colour, shape )
);
insert into bricks_for_sale values ( 'red', 'cube', 4.95 );
insert into bricks_for_sale values ( 'blue', 'cube', 7.75 );
insert into bricks_for_sale values ( 'blue', 'pyramid', 9.99 );
commit;
数据如下:
SQL> select * from bricks_for_sale;
COLOUR SHAPE PRICE
_________ __________ ________
red cube 4.95
blue cube 7.75
blue pyramid 9.99
SQL> select * from purchased_bricks;
no rows selected
Insert-then-update 或 Update-then-insert
向表中添加行时,有时需要执行“不存在就插入,存在就更新”的逻辑,而不想写单独的insert和update语句,这就是upsert。
例如,以下PL/SQL采用先更新后插入的方式实现了upsert,关键点是用sql%rowcount
是否为0来判断更新是否成功。这里有一个前提,就是表有相应的主键,否则update可能修改多行:
declare
l_colour varchar2(10) := 'blue';
l_shape varchar2(10) := 'pyramid';
l_price number(10, 2) := 9.99;
begin
update purchased_bricks pb
set pb.price = l_price
where pb.colour = l_colour
and pb.shape = l_shape;
if sql%rowcount = 0 then
insert into purchased_bricks
values ( l_colour, l_shape, l_price );
end if;
end;
/
select * from purchased_bricks;
COLOUR SHAPE PRICE
_________ __________ ________
blue pyramid 9.99
另一种实现upsert的方式是先插入再更新,关键点是判断insert是否会引起重复索引:
declare
l_colour varchar2(10) := 'blue';
l_shape varchar2(10) := 'pyramid';
l_price number(10, 2) := 15.49;
begin
insert into purchased_bricks
values ( l_colour, l_shape, l_price );
exception
when DUP_VAL_ON_INDEX then
update purchased_bricks pb
set pb.price = l_price
where pb.colour = l_colour
and pb.shape = l_shape;
end;
/
select * from purchased_bricks;
commit;
取决于数据,两种方法都有可能有多余的操作,更好的方法是用Merge。
Merging New Values
Merge 是一种允许您根据需要执行插入或更新的语句。 要使用它,您需要在join子句中说明目标表中的值与源表中的值之间的关系。 然后在 when notmatched 子句中插入行。 并在when matched时更新它们。
目标表是您将添加或更改其行的表。 您将源数据合并到此。源数据必须是表,查询返回的表也行。
Merge示例如下,包含了上面所说的几个元素。源表时用查询产生的,join子句的联结键恰好是主键,以判断其唯一性。总之还比较容易理解:
merge into purchased_bricks pb
using (
select 'blue' colour, 'cube' shape, 15.95 price
from dual
) bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price )
when matched then
update set pb.price = bfs.price;
select * from purchased_bricks;
COLOUR SHAPE PRICE
_________ __________ ________
blue pyramid 15.49
blue cube 15.95
Merging Two Tables
如果源表不是一个查询,那么两个表的合并也很简单:
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price )
when matched then
update set pb.price = bfs.price;
select * from purchased_bricks;
commit;
COLOUR SHAPE PRICE
_________ __________ ________
blue pyramid 9.99
blue cube 7.75
red cube 4.95
下面是不用upsert的实现方式,要复杂很多,不过逻辑也还清楚:
update purchased_bricks pb
set pb.price = (
select bfs.price
from bricks_for_sale bfs
where pb.colour = bfs.colour
and pb.shape = bfs.shape
)
where exists (
select null
from bricks_for_sale bfs
where pb.colour = bfs.colour
and pb.shape = bfs.shape
);
insert into purchased_bricks (
colour, shape, price
)
select bfs.colour, bfs.shape, bfs.price
from bricks_for_sale bfs
where not exists (
select null
from purchased_bricks pb
where pb.colour = bfs.colour
and pb.shape = bfs.shape
);
select * from purchased_bricks;
rollback;
Merge Restrictions
Merge也有限制,你只能修改:
- 不在join子句中的列
- 每行一次
如果修改在join子句中的列,报错如下:
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price )
when matched then
update set pb.colour = bfs.colour, pb.shape = bfs.shape;
Error at Command Line : 3 Column : 9
Error report -
SQL Error: ORA-38104: Columns referenced in the ON Clause cannot be updated: "PB"."COLOUR"
如果1行被修改多次(因为join条件变了),报错如下:
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price )
when matched then
update set pb.price = bfs.price;
Error report -
ORA-30926: unable to get a stable set of rows in the source tables
Conditional Merging
如果想做有条件的更新和插入,在matched子句中加where条件即可。
例如,以下SQL只upsert颜色为blue的行:
update bricks_for_sale
set price = 100;
insert into bricks_for_sale values ( 'red', 'pyramid', 5.99 );
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price )
where bfs.colour = 'blue'
when matched then
update set pb.price = bfs.price
where bfs.colour = 'blue';
select * from purchased_bricks;
rollback;
COLOUR SHAPE PRICE
_________ __________ ________
blue pyramid 100
blue cube 100
red cube 4.95
在merge前,两表的数据为:
SQL> select * from bricks_for_sale;
COLOUR SHAPE PRICE
_________ __________ ________
red cube 100
blue cube 100
blue pyramid 100
red pyramid 5.99
SQL> select * from purchased_bricks;
COLOUR SHAPE PRICE
_________ __________ ________
blue pyramid 9.99
blue cube 7.75
red cube 4.95
Single Operation Merge
merge 中的 when matched和 when not matched子句都是可选的。 因此,您可以实现仅插入或仅更新的merge。例如:
-- 仅插入的merge
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price );
-- 仅更新的merge
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when matched then
update set pb.price = bfs.price;
select * from purchased_bricks;
rollback;
如果不用merge,也可以使用以下SQL实现仅更新的合并。但确定是需要访问bricks_for_sale表两次,而merge语句只需1次:
update purchased_bricks pb
set pb.price = (
select bfs.price
from bricks_for_sale bfs
where pb.colour = bfs.colour
and pb.shape = bfs.shape
)
where exists (
select null
from bricks_for_sale bfs
where pb.colour = bfs.colour
and pb.shape = bfs.shape
);
Merge + Delete
您还可以使用合并从目标表中删除行,这只会发生在目标表中的行在源表中具有匹配行时。
为此,请在when matched子句中的更新后添加删除子句。例如:
when matched then
update set pb.price = bfs.price
delete where pb.colour = 'blue'
删除使用更新后目标表中的值,也就是说,只会影响现有的行,merge操作中新增的行不受影响。
例如,在合并前的数据如下:
insert into bricks_for_sale values ( 'blue', 'cuboid', 5.99 );
select * from purchased_bricks;
COLOUR SHAPE PRICE
_________ __________ ________
blue pyramid 100
blue cube 100
red cube 4.95
select * from bricks_for_sale;
COLOUR SHAPE PRICE
_________ __________ ________
red cube 100
blue cube 100
blue pyramid 100
red pyramid 5.99
blue cuboid 5.99
使用以下SQL合并:
merge into purchased_bricks pb
using bricks_for_sale bfs
on ( pb.colour = bfs.colour and pb.shape = bfs.shape )
when not matched then
insert ( pb.colour, pb.shape, pb.price )
values ( bfs.colour, bfs.shape, bfs.price )
when matched then
update set pb.price = bfs.price
delete where pb.colour = 'blue' ;
select * from purchased_bricks;
COLOUR SHAPE PRICE
_________ __________ ________
red cube 100
red pyramid 5.99
blue cuboid 5.99
rollback;
环境清理
drop table purchased_bricks;
drop table bricks_for_sale;
以上是关于Oracle开发者中级第8课(Merge)实验的主要内容,如果未能解决你的问题,请参考以下文章