关键字before和after用于标识触发时间,顾名思义,before代表触发器里面的命令在DML修改数据之前执行,after代表触发器里面的命令在DML修改数据之后执行。
读者可以结合应用场景选择使用before或者after。
下面编者分别给出before和after的举例。
before举例:
在emp表上面创建触发器,当输入的工资小于100时,自动将工资修改为100。
create or replace trigger tri_emp_sal_check
before INSERT OR UPDATE ON emp
for each row
BEGIN
IF :new.sal <100 THEN
:new.sal :=100;
END IF;
END;
执行DML语句
update emp set sal =90 where empno =3030;
commit;
发现工号3030员工的工资成功修改为100。
上面的触发器,我们尝试将before修改为after。编译的时候报错,不能通过。错误信息如下:
编者前面已经说过,after代表触发器里面的命令在DML修改数据之后执行,既然DML操作对数据的更改已经完成,当然不允许再修改new对象的属性值了,所以此处编译不通过。
after举例:
在emp表上面创建触发器,当修改员工工资时,记录日志。为了更明显的显示效果,编者在此触发器中使用了自治事务。并且在emp表的sal列上增加了check约束,限制sal的值大于等于100。
create or replace trigger tri_emp_sal_change
after UPDATE ON emp
for each row
DECLARE
V_ID NUMBER;
pragma autonomous_transaction;
BEGIN
IF updating('SAL')THEN
SELECT LOG_ID.NEXTVAL INTO V_ID FROM DUAL;
insert into log
(ID,CONTENT, IP, COMPUTERNAME)
values
(v_ID,
:new.empno ||'工资' ||'由' || :old.sal ||'变更为' ||
:new.sal,
sys_context('userenv','ip_address'),
sys_context('userenv','terminal'));
commit;
END IF;
END;
接着编者执行如下update命令
update emp set sal =90 where empno =3030;
报违反检查约束条件错误,如下图所示。
编者查看log表,发现log表为空。如下图所示。
说明使用after的情况下,触发器里面的命令在DML修改数据之后执行,此时log中无记录,是因为DML修改数据时违反检查约束条件,触发器里面的命令没有机会执行。
接着编者将触发器中的after替换成before。
执行相同的update命令
update emp set sal =90 where empno =3030;
同样违反检查约束条件,报错如下图所示。
编者接着查询log表,发现成功记录了修改日志。
说明使用before的情况下,触发器里面的命令在DML修改数据之前执行,此处虽然DML修改数据时违反检查约束条件,没有执行成功,但是由于触发器里面的命令先于DML修改数据之前执行,并且此处使用了自治事务,使得触发器里面的命令得以成功执行。
到底应该使用before还是after,要根据具体情况而定,一般需要更改要操作的数据时使用before。如果记录日志要使用after。只有使用了after才能确保DML语句成功执行,这样记录日志才有意义。