最近一个项目,遇到一个棘手的问题,需要用到组合触发器,但是dml可能是insert、update、delete,这组合触发器表现都很正常。在有一种业务需要用到merge,一个回合完成insert、update、delete,这情况下compound trigger就出问题。

先来了解一下compound trigger官方的说法,before statement、 before each row、 after each row、after statement 都可以访问触发器声明部分的变量、子程序等。但是测试结果表明,如果dml只执行了单一的 insert、update、delete,以上说法没有问题,但是如果 dml 执行的是merge,且merge结果有insert、update、delete,结果就不一样了。

下面上测试实例。

--1-1.创建数据源表
create table sourData(FIELD_KEY int, FIELD_VALUE int);
/

--1-2.数据源表中插入数据
insert into sourData(FIELD_KEY, FIELD_VALUE) values (1, 1);
insert into sourData(FIELD_KEY, FIELD_VALUE) values (2, 2);
insert into sourData(FIELD_KEY, FIELD_VALUE) values (3, 3);
insert into sourData(FIELD_KEY, FIELD_VALUE) values (4, 4);
insert into sourData(FIELD_KEY, FIELD_VALUE) values (5, 5);
insert into sourData(FIELD_KEY, FIELD_VALUE) values (6, 6);
/

--2.1 创建目标表
create table destData(FIELD_KEY int, FIELD_VALUE int);
/

--2.2 目标表中插入数据
insert into destData(FIELD_KEY, FIELD_VALUE) values (1, 0);
insert into destData(FIELD_KEY, FIELD_VALUE) values (2, 0);
insert into destData(FIELD_KEY, FIELD_VALUE) values (3, 0);
/
commit;
/

--3.在目标表上创建复合触发器
create or replace trigger "trgUpdateData" for insert or update or delete on destData compound trigger

type LIST_TYPE is table of destData%ROWTYPE index by simple_integer;
  list        LIST_TYPE;
  vSESSION_ID varchar2(20);
  I           simple_integer := 0;

before statement is
  begin
    case 
      when INSERTING then
        vSESSION_ID := '1';
        dbms_output.put_line('insert session id is ' || vSESSION_ID);
      when UPDATING then
        vSESSION_ID := '2';
        dbms_output.put_line('update session id is ' || vSESSION_ID);
      when DELETING then
        vSESSION_ID := '3';
        dbms_output.put_line('delete session id is ' || vSESSION_ID);
      else
        vSESSION_ID := '4';
        dbms_output.put_line('unkown session id is ' || vSESSION_ID);
    end case;
  end before statement;
  
  before each row is
  begin
    case 
      when INSERTING then
        I := I + 1;
        list(I).FIELD_KEY   := :new.FIELD_KEY;
        list(I).FIELD_VALUE := :new.FIELD_VALUE;
      when UPDATING then
        I := I + 1;
        list(I).FIELD_KEY   := :new.FIELD_KEY;
        list(I).FIELD_VALUE := :new.FIELD_VALUE;
        I := I + 1;
        list(I).FIELD_KEY   := :old.FIELD_KEY;
        list(I).FIELD_VALUE := :old.FIELD_VALUE;
      when DELETING then
        I := I + 1;
        list(I).FIELD_KEY   := :old.FIELD_KEY;
        list(I).FIELD_VALUE := :old.FIELD_VALUE;
      else
        null;
    end case;
  end before each row;

after statement is
    datacount simple_integer := list.count();
  begin
    case 
      when INSERTING then
        dbms_output.put_line('use insert session id is ' || nvl(vSESSION_ID, 'NULL'));
      when UPDATING then
        dbms_output.put_line('use update session id is ' || nvl(vSESSION_ID, 'NULL'));
      when DELETING then
        dbms_output.put_line('use delete session id is ' || nvl(vSESSION_ID, 'NULL'));
      else
        dbms_output.put_line('use unkown session id is ' || nvl(vSESSION_ID, 'NULL'));
    end case;
    dbms_output.put_line('list count:' || datacount);
    for I in 1..datacount loop
      dbms_output.put_line('KEY:' || list(I).FIELD_KEY || ', VALUE:' || list(I).FIELD_VALUE);
    end loop;
    list.delete();
  end after statement;
end;

/

--执行merge语句,注意4、5、6为插入数据,2、3为修改数据,1为删除记录;

merge into destData A

using sourData B
  on (A.FIELD_KEY = B.FIELD_KEY)
  when not matched then
    insert (FIELD_KEY, FIELD_VALUE) values (B.FIELD_KEY, B.FIELD_VALUE)
      where B.FIELD_VALUE in (4, 5, 6)
  when matched then
    update set A.FIELD_VALUE = B.FIELD_VALUE
      where B.FIELD_VALUE in (1, 2, 3)
    delete 
      where A.FIELD_VALUE = 1;

/

--这是执行过程中,compound trigger的输出:

insert session id is 1
update session id is 2
delete session id is 3
use insert session id is NULL
list count:3
KEY:6, VALUE:6
KEY:4, VALUE:4
KEY:5, VALUE:5
use update session id is NULL
list count:6
KEY:1, VALUE:1
KEY:1, VALUE:0
KEY:2, VALUE:2
KEY:2, VALUE:0
KEY:3, VALUE:3
KEY:3, VALUE:0
use delete session id is 3
list count:1
KEY:1, VALUE:1

执行结果虽然destData表里面是没有问题的,但是注意看输出里面的两个NULL,但是怎么会有NULL呢?在before statement里面有为变量vSESSION_ID赋值的,怎会在after statement 里面,当inserting 和 updating的时候,vSESSION_ID值为NULL了?事实上,在后继的测试里面,在before each row里面 vSESSION_ID的值在inserting和updating的时候也是NULL!

继续测试:

注意输出结果里面,对list的输出,不管是inserting/upating/deleting都是有数据的!也复合预期!但是list是在each row里面处理数据的,会不会each row里面处理的数据不会丢失呢?如果vSESSION_ID的值也在each row里面赋值,会不会丢失呢?

那就改造这组合触发器,在each row里面判断vSESSION_ID的值,如果是NULL就重新赋值:

rollback;

/

create or replace trigger "trgUpdateData" for insert or update or delete on destData compound trigger

type LIST_TYPE is table of destData%ROWTYPE index by simple_integer;
  list        LIST_TYPE;
  vSESSION_ID varchar2(20);
  I           simple_integer := 0;

before statement is
  begin
    case 
      when INSERTING then
        vSESSION_ID := '1';
        dbms_output.put_line('insert session id is ' || vSESSION_ID);
      when UPDATING then
        vSESSION_ID := '2';
        dbms_output.put_line('update session id is ' || vSESSION_ID);
      when DELETING then
        vSESSION_ID := '3';
        dbms_output.put_line('delete session id is ' || vSESSION_ID);
      else
        null;
    end case;
  end before statement;
  
  before each row is
  begin
    case 
      when INSERTING then
        I := I + 1;
        list(I).FIELD_KEY   := :new.FIELD_KEY;
        list(I).FIELD_VALUE := :new.FIELD_VALUE;
      when UPDATING then
        I := I + 1;
        list(I).FIELD_KEY   := :new.FIELD_KEY;
        list(I).FIELD_VALUE := :new.FIELD_VALUE;
        I := I + 1;
        list(I).FIELD_KEY   := :old.FIELD_KEY;
        list(I).FIELD_VALUE := :old.FIELD_VALUE;
      when DELETING then
        I := I + 1;
        list(I).FIELD_KEY   := :old.FIELD_KEY;
        list(I).FIELD_VALUE := :old.FIELD_VALUE;
      else
        null;
    end case;
    if vSESSION_ID is null then
      case 
        when INSERTING then
          vSESSION_ID := '1';
          dbms_output.put_line('recreate insert session id is ' || vSESSION_ID);
        when UPDATING then
          vSESSION_ID := '2';
          dbms_output.put_line('recreate update session id is ' || vSESSION_ID);
        when DELETING then
          vSESSION_ID := '3';
          dbms_output.put_line('recreate delete session id is ' || vSESSION_ID);
        else
          null;
      end case;
    end if;
  end before each row;

after statement is
    datacount simple_integer := list.count();
  begin
    case 
      when INSERTING then
        dbms_output.put_line('use insert session id is ' || nvl(vSESSION_ID, 'NULL'));
      when UPDATING then
        dbms_output.put_line('use update session id is ' || nvl(vSESSION_ID, 'NULL'));
      when DELETING then
        dbms_output.put_line('use delete session id is ' || nvl(vSESSION_ID, 'NULL'));
      else
        null;
    end case;
    dbms_output.put_line('list count:' || datacount);
    for I in 1..datacount loop
      dbms_output.put_line('KEY:' || list(I).FIELD_KEY || ', VALUE:' || list(I).FIELD_VALUE);
    end loop;
    list.delete();
  end after statement;
end;

/

然后执行merge语句:

merge into destData A

using sourData B
  on (A.FIELD_KEY = B.FIELD_KEY)
  when not matched then
    insert (FIELD_KEY, FIELD_VALUE) values (B.FIELD_KEY, B.FIELD_VALUE)
      where B.FIELD_VALUE in (4, 5, 6)
  when matched then
    update set A.FIELD_VALUE = B.FIELD_VALUE
      where B.FIELD_VALUE in (1, 2, 3)
    delete 
      where A.FIELD_VALUE = 1;

/
输出结果如下:
insert session id is 1
update session id is 2
delete session id is 3
recreate update session id is 2
recreate insert session id is 1
use insert session id is 1
list count:3
KEY:6, VALUE:6
KEY:4, VALUE:4
KEY:5, VALUE:5
use update session id is 2
list count:6
KEY:1, VALUE:1
KEY:1, VALUE:0
KEY:2, VALUE:2
KEY:2, VALUE:0
KEY:3, VALUE:3
KEY:3, VALUE:0
use delete session id is 3
list count:1
KEY:1, VALUE:1

这次输出结果复合预期了,注意到输出结果里面有两次recreate session,正好就是丢失的inserting和updating的vSESSION_ID的。

虽然有解决办法了,但是这样的做法,很显然,before statement 没用了,而且在each row里面需要每行数据都要对vSESSION_ID的值进行判断,降低了效率。

期待Oracle继续改进吧。

Oracle compound trigger的大坑(Oracle-12c)相关推荐

  1. oracle中04091,ORA-04091和Compound Trigger(Oracle 11g)

    Trigger 常见有两种:行(Row Trigger)和语句(Statement Trigger) 还有:Instead of Trigger和Event trigger. 例子1-Row Trig ...

  2. Oracle Linux 6下安装Oracle 12C实战

    Oracle Linux 6下安装Oracle 12C实战,经过N次安装尝试吐血总结,希望对大家有所帮助,同时做下记录备忘: 总结:安装前的准备工作是关键!!!Check and recheck! 参 ...

  3. oracle asm密码是什么,ORACLE 12C ASM 新特性:共享密码文件

    在ORACLE 12C之前大家都知道密码文件是存放在?/dbs或者?/database中,如果要修改修改sysdba权限的用户密码时候,会去修改密码文件,而在rac数据库的sys密码文件是存在各个节点 ...

  4. oracle安装时创建应答文件,12c 应答方式安装 (二) 创建cdb+pdb+netca

    上一篇已经用应答文件安装了数据库软件,这一篇博客接着安装12c新推出的CDB和PDB. 一,配置netca 1. 复制并修改netca配置文件 [oracle@snow response]$ cp - ...

  5. Oracle 触发器trigger介绍

    一.触发器定义 Oracle触发器是使用者对Oracle数据库的对象做特定的操作时,触发的一段PL/SQL程序代码器.触发的事件包括对表的DML操作,用户的DDL操作以及数据库事件等. 二.触发器分类 ...

  6. Oracle 9i,10g/11g,12c中的i,g,c分别代表什么含义

    Oracle 9i,10g/11g,12c中的i,g,c分别代表什么含义 i是internet的意思,表明当时是internet互联网盛行的年代. g是grid,网格运算.为了迎合分布式计算而推出的版 ...

  7. oracle版本迭代更新 10g,11g,12c,18c,19c

    oracle版本迭代更新 10g,11g,12c,18c,19c 时间:2021年2月20日 官网截图 看清楚官网上各各大版本下的稳定版本顺序,方便对照自己oracle版本针对分析 区别: Oracl ...

  8. oracle ocm查寻,如何查询Oracle 12c OCP , 11g OCP , OCM考试成绩和证书

    从2013年10月30日开始, Oradle的考试成绩不在VUE考试系统当场出来,需要考试在考试结束后到CertView.上查询.其中的关键是需要考生先到Oracle网站注册个SSO (单点登录)账号 ...

  9. oracle 表列 自增,ORACLE表建立自增列

    create tablespace studentDB datafile 'E:\datafiles_1.dbf' size 10m; create user Huang_Ying_Bo identi ...

最新文章

  1. 第一学期网络技术知识总汇
  2. linux下c 多线程如何映射文件夹,c - 在Linux中使用多个线程进行信号处理
  3. linux 无法启动vnc_vnc登录,10个步骤教你在Linux中VNC登陆
  4. 计算机动画整个的发展历史,三维动画的发展史
  5. 文件、目录——Linux基本命令(5)
  6. oracle未找到时区,Oracle ADF 未找到时区错误
  7. (软件工程复习核心重点)第三章需求分析-第二节:实体联系图(ER图)
  8. oracle 里数据的编码格式,oracle 数据的编码格式
  9. 黑龙江高职计算机对口升学,2020年黑龙江中职对口招生录取院校投档分数线
  10. c语言把一段编码注释,C语言编码规范——着重注意点整理
  11. 论高性能机房标识标签管理办法一现状篇
  12. 通俗易懂RESTful,如何设计RESTful风格API
  13. C++ 訪问控制权限图解
  14. 2020年中国旅游行业网络关注度分析报告
  15. 海思视频监控芯片如何一步步成为行业霸主
  16. oppo r11s鸿蒙固件,OPPO R11s Plus原厂rom固件系统升级包下载
  17. java箱子容积_Java开发笔记(一百三十八)JavaFX的箱子
  18. mysql全称量词_MySQL操作记录的方法集合,供以后查看
  19. 大事务的处理方式对比
  20. java中strictfp么意思_java中的strictfp的作用

热门文章

  1. AutoSAR系列讲解(入门篇)1.1-AutoSAR发展
  2. 【工大SCIR论文解读】WWW20 关键词生成提升电商会话推荐
  3. 交通运输综合管理信息平台建设方案(附下载)
  4. 神通数据库自助在线查询
  5. 计算机不能启动 无法验证数字签名,windows启动管理器,状态0xc0000428 无法验证此文件的数字签名解决办法。...
  6. 解决模糊查询问题 element UI 从服务器搜索数据,输入关键字进行查找
  7. STC89C52单片机
  8. PHP日期转换为时间戳
  9. wps将word文档转换为图片格式
  10. 今日头条能干掉微信么?