“怎么会发生这种事情?””我们的触发器触发了两次!“
那些难以理解的事情,也许通过这里的实验可以窥探一二。
为了做这个实验,先创建表
sql> create table t(x int,y int);
插入实验数据
sql> insert into t values(2,5);
已创建 1 行。
sql> commit;
提交完成。
创建触发器
sql> create or replace trigger t_buffer
2 before update on t for each row
3 begin
4 dbms_output.put_line
5 ('old.x='||:old.x||',old.y='||:old.y);
6 dbms_output.put_line
7 ('new.x='||:new.x||',new.y='||:new.y);
8 end;
开启输出打印
sql> set serveroutput on;
更新,但不要提交,这个sqlplus的session我们成为session A
sql> update t set x=x+1;
old.x=2,old.y=10
new.x=3,new.y=10
已更新 1 行。
此时,在打开一个会话,我们称为session B。执行如下命令,发现挂起。
sql> set serveroutput on;
sql> update t set x=x+1 where x>0;
此时,在session A中,提交事务
sql> commit; 提交完成。
于是,我们看到了session B中挂起的语句执行,并出现了打印。为方便查看,我们将update语句也放在了这里。
sql> update t set x=x+1 where x>0;
old.x=2,old.y=10
new.x=3,new.y=10
old.x=3,old.y=10
new.x=4,new.y=10
已更新 1 行。
为什么触发器被触发了两次呢?我们再做一个实验。(实验开始前,记得在session B中做commit或rollback)
重新创建触发器。
sql> create or replace trigger t_buffer
2 before update on t for each row
3 begin
4 dbms_output.put_line('fired');
5 end;
6 /
触发器已创建
在Session A中执行updte
sql> update t set x=x+1;
fired
已更新 1 行。
在Session B中执行update,程序挂起。提交阻塞会话(即Session A)后,可以看到以下输出。这个结果和我在书中看到的不一样,书中是打印了一遍。
sql> update t set x=x+1 where x>0;
fired
fired
已更新 1 行。
实验做到这里,我想说,我做的结果和书上的有不一样(我的“fired”打印了两遍,而书中只有一遍,朋友们有时间的话,可以将自己得到的结果也拿出来分享看看)。Oracle_Database_9i10g11g编程艺术深入数据库体系结构第2版237页中。有分析,我贴个图。
所以,书中建议我们使用AFTER FOR EACH ROW而不是BEFORE FOR EACH ROW做触发器。于是我试了试
sql> create or replace trigger t_buffer
2 after update on t for each row
3 begin
4 dbms_output.put_line('old.x='||:old.x||',new.x='||:new.x);
5 end;
Session A
sql> update t set x=x+1;
old.x=11,new.x=12
已更新 1 行。
SessionB,当然执行update后,会挂起,直到SessionA提交事务以后,才会打印出内容。
sql> update t set x=x+1 where x>0;
old.x=12,new.x=13
已更新 1 行。
虽然在做实验的时候,有些结果不一致,但我还是觉得有必要将下面的一整页内容贴出来。方便以后查阅。