OracleDML 触发器的执行顺序 一、引言 Oracle 触发器是一个存储在数据库中的 PL/SQL 块,用于 响应特定事件时被自动调用,以 实现一些复杂的业务规则。 按照导致触发器触发的特定事件的不同,orade 支持 种基本的触发器事件: 数据操纵语句(DML)触发器 数据定义语句(DDL)触发器 数据库事件触发器 本文要介绍的是数据操纵语句(DML)触发器(以下简称 DML 触发器)的几种类型及触发顺 二、DML触发器介绍 DML 触发器是一个对关联表发出insert、u 冈ate 或delete 语句时自 动触发的存储过程。DML 触发器的主要作用是能够 实现复杂的参照完整性和数据的一致 性,除此之外还包括强化 约束,跟踪变化,级联运行等作用。 创建DML 触发器的语法如下: CREATE TRIGGERFOR EACH PL/SOL 代码块 OracleDML 触发器的类型,有不同的分类法,按照触发器主体执行级别有:语句级触发器 和行级触发器;按照执行的时间有:BEFORE AFTER触发器;按照导致触发器触发的事 件有:INSERT、UPDATE DELETE。使用以上选项的组合,可以为数据库表指定12 (12=2*2*3)类触发器。但考虑INSERT、UPDATE 和DELETE 仅表示导致触发的DML 句,不会影响触发的先后顺序,因此,我们只需讨论:语句级触发器、行级触发器好BEFORE、 AFTER 触发器组合(共4 类)的触发顺序问题。 三、执行顺序 BEFORE 触发器在执行触发语句之前触发。反之,AFTER 触发器在执行触发语句之后触发。 eachrow(行级)选项指定触发器在每一行上触发一次。默认情况下(语句级)触发器 对每条语句触发一次。使用以上选项的组合的4 类触发器如下: BEFORE DML 语句 Table//语句级之前触发 BEFORE DML 语句 EachRow //行级之前触发 AFTER DML 语句 EachRow //行级之后触发 AFTER DML 语句 Table//语句级之后触发 类触发器的触发,以及触发SQL语句运行的先后顺序如表1 所示。 触发器触发顺序触发器类型 语句级之前在触发DML 语句执行之前执行一次 /行级之前 在受触发DML 语句影响的每一条记录被修改,删除或插入之前执行一次 行级之后 在受触发DML 语句影响的每一条记录被修改,删除或插入之后执行一次 语句级之后 在触发DML 语句执行之后执行一次 理解了这个顺序之后,在实际运用中就能够有效地利用触发器来完成工作。下面通过一个示 例来比较一下这几种DML 触发器的使用。 四、示例 有一个员工薪水表EMP 如表2 所示。 员工薪水表EMPNO SALARY 900010 10000 业务规则 1:给员工加薪时,要扣税,扣税的规则是,原来薪水在 5000 以上(>=5000)的扣除总薪水的 10%,小于5000 的扣除总薪水的5%。 问题分析:该业务规则涉及到对 EMP 表的每一条记录进行判断,因此要使用行级触发 器;:OLD 包含字段更新前的值,而:NEW包含字段更新后的值。通过对:NEW和:OLD 较,我们可以知道是否在加薪。通过:OLD的值,可以知道每一个员工原来的薪水情况。因 为要扣除总薪水一部分,所以要修改:NEW的值,因此必须使用BEFORE 触发器。 示例代码: --创建BEFORE 行级触发器 CREATE REPLACETRIGGER before_add_salary BEFORE UPDATE EACHROW BEGIN :NEW.salary:=:NEW.salary*0.95;--扣除总薪水的5% ELSE :NEW.salary:=:NEW.salary*0.9; --扣除总薪水的10% END 业务规则2:如果目前全体员工的总薪水大于60000,则不能加薪了;如果总薪水小于30000,则不能减薪了。 问题分析:该业务规则只涉及到对EMP 表的所有记录(总薪水)情况进行判断,因此不必 使用行级触发器;而要想知道到底修改进行的是加薪还是减薪操作,又必须使用AFTER 句级触发器求出修改后的总薪水,然后与修改前的总薪水进行比较。解决方案:编写 BEFORE 语句级触发器,将修改前的总薪水保存在一个包变量中;编写 AFTER 语句级触发器,将修改后的总薪水保存在另一个包变量中,然后比较两个包变量, 区分是加薪还是减薪操作。 示例代码: --创建包变量 CREATE REPLACEPACKAGE EMP_PACKAGE old_numNUMBER; new_num NUMBER; END; --创建BEFORE语句级触发器 CREATE REPLACETRIGGER before_update_salary BEFORE UPDATE EMPBEGIN --将修改之前的总薪水写入EMP_PACKAGE.old_sum 包变量 SELECT SUM(salary) EMP_PACKAGE.old_sumFROM EMP; END; --创建AFTER语句级触发器 CREATE REPLACETRIGGER after_update_salary AFTER UPDATE --语句级AFTER 触发 EMPBEGIN --将修改之后的总薪水写入EMP_PACKAGE.new_sum 包变量 SELECT SUM(salary) EMP_PACKAGE.new_sumFROM EMP; --抛出异常,并回滚操作RAISE_APPLICATION_ERROR(-20004,'目前的总薪水大于60000,不能再加薪了!'); END ELSE--减薪 --抛出异常,并回滚操作RAISE_APPLICATION_ERROR(-20004,'目前的总薪水小于30000,不能再减薪了!'); END 以上示例代码全部在Oracle9i 下测试通过。