【Oracle 11g】我们的触发器竟然触发了两次!——诱因:sql重启动

“怎么会发生这种事情?””我们的触发器触发了两次!“
那些难以理解的事情,也许通过这里的实验可以窥探一二。
先回顾一下读一致性:Oracle可以使用undo信息来提供非阻塞的查询和一致(正确)的读。查询时,Oracle会从缓存中读出块,它能保证这个块版本足够”旧“,能够被查询看到。

为了做这个实验,先创建表

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。执行如下命令,发现挂起。
此处的update后面增加了where查询字句。一般来讲,UPDATE或者WHERE子句中引用的列能用于确定修改是否需要重启动。Oracle使用这些列完成一个一致读,然后以当前模式获取块时,如果检测到任何列有修改,就会重启动这个语句。[1]

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做触发器

所以,书中建议我们使用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 行。

虽然在做实验的时候,有些结果不一致,但我还是觉得有必要将下面的一整页内容贴出来。方便以后查阅。
这里写图片描述


[1]《Oracle_Database_9i10g11g编程艺术深入数据库体系结构》第2版,Thomas Kyte著,苏金国 王小振等译,237页

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值