一、触发器
触发器是当特定事件出现时自动执行的代码块。比如,每次对员工表进行增删改的操作时,向日志表中添加一条记录。触发器和存储过程是由区别的:触发器是根据某些条件自动执行的,存储过程是手动条用的。
(一)触发器的类型:
1、行级触发器:操作数据行的时候触发。
2、语句级触发器:一个DML语句出发一次的触发器。行级触发器和语句级触发器一起也成为DML触发器。
3、INSTEADOF 触发器:主要是在视图上定义的触发器。
4、模式触发器:用户事务触发器。
5、数据库触发器:创建在数据库事件上的触发器。
(二)触发器可以分为三个部分:
1、触发器语句:触发器语句是哪些可以导致触发器的事件,即在表或者视图上执行INSERT、DELETE、UPDATE之类的DML语句,在模式对象上执行的DDL语句或数据库事件。所有可以导致触发器的事件都是触发器的语句。
2、触发器限制:触发器限制条件包含一个布尔表达式,该值必须为”真“才能激活触发器,如果该值为”假“或”未知“,将不运行触发器操作。
3、触发器操作:触发器操作时触发器的主体,包含一些SQL语句和代码,这些代码在执行触发器有且触发器限制条件为”真“时运行。行级触发器允许触发器操作中的语句访问行的列值。
(三)触发器的常用功能如下:
1、允许/限制对表的修改。
2、自动生成派生列。
3、强制数据一致性。
4、强制复杂的完整性约束条件。
5、提供审计和日志记录。
6、防止无效的事件处理。
7、启动复杂的业务逻辑。
由于触发器是对数据进行监控的一个对象,随时监控着数据库中的数据和表,造成使用触发器的性能低下。这点尤为重要。
二、创建触发器:
语法:
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF}
{INSERT | DELETE | UDPATE | [OF column[,column]]}
[OR {INSERT | DELETE | UPDATE [OF column[,column]]}]
ON [schema.]table_or_view_name
[REFERENCING [NEW AS new_row_name] [OLD AS old_row_name]]
[FOR EACH ROW]
[WHEN (condition)]
[DECLARE
variable_declation]
BEGIN
statements;
[EXCEPTION
exception_handlers]
END[trigger_name];
说明:
trigger_name:触发器的名称
BEFORE | AFTER :表示在事件发生之前或之后激活触发器
INSTEAD OF:表示可以执行触发器代码来代替导致触发器调用的事件。
INSERT | DELETE | UDPATE :指定构成触发器事件的数据库操作类型,UPDATE还可以指定列的列表。
REFERENCING :指定新行(即将更新)和旧行(更新前)的其他名称,默认为NEW和OLD。
table_or_view_name:触发器所监控的表或者视图的名称。
FOR EACH ROW:表示是否对受影响的每一行都执行触发器,即行级触发器。如果不使用此语句,则为语句触发器,使用此语句则为行级触发器。
WHEN (condition):限制执行触发器的条件,该条件可以包括新旧数据值的检查。
DECLARE:声明触发器所使用的局部变量
statements:触发器所执行的PL-SQL语句。
exception_handlers:异常处理代码。
例1:生成一个自动编号(行级触发器)
--创建测试表,学生表 CREATE TABLE student ( stu_id NUMBER CONSTRAINT pk_id PRIMARY KEY, stu_name VARCHAR2(20) NOT NULL, stu_phone VARCHAR2(20) NOT NULL );
--创建一个序列,从1开始累加,每次加1 CREATE SEQUENCE seq_student START WITH 1 --序列开始,从1开始计数 INCREMENT BY 1 --每次增加1 NOMAXVALUE --不设置最大值 NOCYCLE --只累加,不循环 CACHE 20; --缓存20
create or replace trigger student_insert_seq before insert on student --在插入之前 for each row --行级触发器 begin :new.stu_id := seq_student.nextval; --将插入的stu_id属性设置为序列的下一个值 end student_insert_seq;
--插入数据 INSERT INTO student (stu_name,stu_phone) VALUES('张三','110'); INSERT INTO student (stu_name,stu_phone) VALUES('李四','120'); --查询,数据插入成功 SELECT * FROM student
例2:实现表操作日志
--新建日志表序列 CREATE SEQUENCE logs_id_squ INCREMENT BY 1 START WITH 1 MAXVALUE 9999999 NOCYCLE NOCACHE; --日志表 CREATE TABLE logs( LOG_ID NUMBER(10) PRIMARY KEY, LOG_TABLE VARCHAR2(10) NOT NULL, LOG_DML VARCHAR2(10), LOG_KEY_ID NUMBER(10), LOG_DATE DATE, LOG_USER VARCHAR2(15) );
--创建日志触发器:表级触发器 create or replace trigger trig_log after INSERT OR UPDATE OR DELETE on emp for each row DECLARE V_USERNAME VARCHAR2(20); BEGIN SELECT USER INTO V_USERNAME FROM dual; --获取当前用户 IF INSERTING THEN INSERT INTO logs VALUES(logs_id_squ.nextval,'emp','insert',:new.Empno,sysdate,V_USERNAME); ELSIF UPDATING THEN INSERT INTO logs VALUES(logs_id_squ.nextval,'emp','update',:new.Empno,sysdate,V_USERNAME); ELSIF DELETING THEN INSERT INTO logs VALUES(logs_id_squ.nextval,'emp','delete',:new.Empno,sysdate,V_USERNAME); END IF; END TRIG_LOG;
三、触发器的注意事项
(1)、create trigger 语句的字符长度不能超多32kb;
(2)、触发器体内的select语句只能为select .....into .....结构,或者为定义游标所使用的select语句;
(3)、触发器中不能使用数据库事务控制语句,如:commit、rollback、savepoint语句;
(4)、由触发器调用的过程或函数也不能使用数据库事务控制语句;
(5)、触发器中不能使用lang、lang row类型;
(6)、触发器可以参照lob类型类的列值,但不能通过:new 来修改lob列中的值;
(6)、触发器所涉及的表收到表约束的限制