使用BEFORE和AFTER触发器
数据库触发器是存储与数据库的命名PLSQL匿名块,当触发事件发生时它们会隐含执行。执行触发器的活动被称为触发触发器。触发事件可以是如下任何一种。
处理数据库的DML语句(如insert,update或者delete)。在触发事件发生之前或者之后,触发器会执行。例如,如果定义对student表执行insert语句之前需要执行一个触发器,则每次往student表插入数据之前都会执行这个触发器。
特定用户在特定模式下,或者任何用户执行DDL语句(如CREATE或者ALTER)。这种触发器经常被用于审计目的,并且专用oracle DBA。它们可以记录各种模式修改,何时执行,以及哪个用户执行的。
系统事件,如数据库启动或者关闭。
用户事件,如登录和注销,即可以定义一个触发器,在用户登录数据库记录用户名和登录时间。
创建触发器的通用语法如下所示(括号中的保留字是可选的):
CREATE [OR REPLACE] TRIGGER Trigger_name {BEFORE|AFTER} Triggering_event on table_name
[FOR EACH ROW] [FOLLOWS another_trigger]
[ENABLE/DISABLE]
[WHEN condition]
DECLARE
declaration statements
BEGIN
executable statements
EXCEPTION
exception-handing statements
END;
保留字create指明正在创建一个新的触发器。保留字replace指定正在修改已有的触发器,replace是可选的。但是,请注意,create和replace在大多数情况下都是存在的,
考虑下面的情形,像下面那样创建触发器:
create trigger trigger_name
几天后,你决定修改这个触发器,如果不在触发器的create子句中包含保留字replace,则当编译这个触发器时就会出现错误消息。错误消息说明,触发器的名字已经被其他对象所使用。当在触发器的create子句中包含replace时,发生错误的几率比较小。因为如果所定义的是新的触发器,则会直接创建,如果是一个已经存在的触发器,则会进行替换。
trigger_name是触发器的名称,before或者after指明何时触发器执行,即在触发事件发生之前,还是之后,trigger_event是针对数据库表的DML语句。table_name是与该触发器相关的数据库表的名称。子句for each row指定行触发器。只适用于所插入,修改或者删除的数据行。where子句指定行触发器必须要满足的条件,这个条件也许对数据库的列做出限制。触发器的这部分通常被称为触发器头。接着,定义触发器体。
请注意三个子句:FOLLOWS,ENABLE和DISABLE,它们是在oracle 11g的create or replace子句中引入的。在oracle 11g之前,需要使用alter trigger 命令来启用或在禁用触发器,触发事件发生会执行该触发器。类型地,当触发器被禁用时,触发事件发生也不会执行该触发器。请注意,当不使用enable或者disable子句来创建触发器时,在默认情况下时启用的。为禁用触发器,学员向下面这样执行alter trigger命令。
alter TRIGGER trigger_name disable;
alter TRIGGER trigger_name enable;
使用follows选项,可以指定触发器被触发的顺序,这个选项适用于相同时间点会执行的触发器。例如,如果在student表上定义两个触发器,并且在数据插入之前触发,如果自己不使用follows子句来指定执行顺序,oracle无法保证这些触发器始终按照相同的次序执行。请注意,FOLLOWS子句中所引用的触发器必须已经存在,并且成功编译。
要认识到,如果删除一个表,则该表上所定义的数据库触发器也会被删除,这一点很重要。
当拥有很多原因,必须使用保留字replace时,也特别仔细。首先,如果不小心对已存在的存储函数,过程或者包使用replace时,就会破坏数据中具有相同名称的不同数据库对象。之所以发生这种情况,是因为在数据库中触发器有独立的命名空间。尽管触发器和过程,函数或者包使用相同的名称不会带来错误,但是也会带来潜在的混乱。因此,这种做法不是很好的,其次,当使用保留字replace,并决定把不同的表和自己的触发器关联起来,则会产生错误消息。例如,假设在student表创建一个触发器student_BI,接着,你决定把不同的表与自己的触发器关联起来,则会产生错误信息。例如,假设在student表创建一个触发器student_BI。接着,你决定修改这个触发器,把它与enrollment表关联起来,这时候,会得到如下错误信息:
BEFORE触发器
考虑下面的触发器范例,建立在本章前面所涉及的student表。这个触发器在针对student表的insert语句之前执行,填充studnet_id,create_date,modify_date,create_by和modified_by等列,使用student_id_SEQ序列所产生的数字来填充列student_id,使用当前日期和当前用户信息来填充create_date,modified_date,create_user和modified_user列。
CREATE OR REPLACE TRIGGER student_bi
BEFORE INSERT ON student
FOR EACH ROW
BEGIN
:new.student_id := student_id_seq.nextval;
:new.created_by := USER;
:new.created_date := SYSDATE;
:new.modified_by := USER;
:new.modified_date := SYSDATE;
END;
在STUDENT表的insert语句执行之前,这个触发器会执行。请注意,这个触发器的名称是student_BI,其中student是定义触发器的数据库表,BI标示before insert。关于触发器命令并没有具体的需求。但是,这种触发器命名方法是描述性的。触发器的名称包含触发事件所涉及到的表名,事件触发时机(即在事件发生前执行,还是发生后执行),以及触发事件本身信息。
触发器体包含伪记录:NEW,使得你可以访问当前正在被处理的数据行。也就是说,当前正被插入student表的数据行。:NEW伪记录是一种trigger_TABLE%TYPE.所以在这种情况下,它是student%type类型的。为了访问伪记录:NEW的单独成员,需要使用点符号。也就是说:NEW.created_by指的是:NEW伪记录成员created_by,记录名称及其成员之间使用点符号。