前几天用到Oracle数据库的update更新,对于这个简单问题,却出现了不少问题,所以现将从网上搜索资料及自已的总结罗列在此以备一时之用.
以下所列sql都是基于下表
create table test (name varchar2(30),code varchar2(10),i_d varchar2(10));
插入数据
insert into test(name,code,i_d) values('zhu1','001','1');
insert into test(name,code,i_d) values('zhu2','002','2');
insert into test(name,code,i_d) values('zhu3','003','3');
commit;
select * from test s;
1. update 更新i_d为1的数据
--方式1
update test set name='zhurhyme1',
code='007' where i_d='1';
commit;
这样可以成功
--方式2
update test set (name,code)=(
'zhurhyme2','007')
where i_d='1';
注意,这样是不行,update set 必须为子查询,所以需要将其改为 :
--方式3
update test set (name,code)=(
select 'zhurhyme3','007' from dual)
where i_d='1';
commit;
2.update 说完了,下面写一下关于for update,for update of
下面的资料是从网上找到的,可是具体的网址现在找不到了,请原谅小弟的粗心,引用人家的东东而不写出处.
for update 经常用,而for updade of 却不常用,现在将这两个作一个区分
a. select * from test for update 锁定表的所有行,只能读不能写
b. select * from test where i_d = 1 for update 只锁定i_d=1的行,对于其他的表的其他行却不锁定
下面再创建一个表
create table t (dept_id varchar(10),dept_name varchar2(50));
c. select * from test a join t on a.i_d=t.dept_id for update; 这样则会锁定两张表的所有数据
d. select * from test a join t on a.i_d=t.dept_id where a.i_d=1 for update; 这样则会锁定满足条件的数据
e. select * from test a join t on a.i_d=t.dept_id where a.i_d=1 for update of a.i_d; 注意区分 d与e,e只分锁定表test中满足条件的数据行,而不会锁定表t中的数据,因为之前在procedure中作一个update,而需要update的数据需要关联查询,所以用了for update造成其他用户更新造成阻塞,所以才查到这段资料.
for update of 是一个行级锁,这个行级锁,开始于一个cursor 打开时,而终止于事务的commit或rollback,而并非cursor的close.
如果有两个cursor对于表的同一行记录同时进行update,实际上只有一个cursor在执行,而另外一个一直在等待,直至另一个完成,它自己再执行.如果第一个cursor不能被很好的处理,第二个cursor也不主动释放资源,则死锁就此产生.
执行如下代码就会死锁(在两个command window中执行)
declare
cursor cur_test
is
select name,code from test where i_d=1 for update of name;
begin
for rec in cur_test loop
update test set name='TTTT1' where current of cur_test;
end loop;
end;
/
declare
cursor cur_test
is
select name,code from test where i_d=1 for update of name;
begin
for rec in cur_test loop
update test set name='TTTT2' where current of cur_test;
end loop;
end;
/
注意两个pl/sql块中没有commit;
为了解决这个死锁问题,要么就是第一个块释放资源,要么就是第二块主动放弃.第一次释放资源很简单,那就执行commit或rollback;而让第二块主动放弃,在for update 后加no wait;这样就会报
ORA-00054 [resource busy and acquire with NOWAIT specified 的错误,这样就没有死锁了.