触发器的基本概念
触发事件包括INSERT、UPDATE或DELETE,触发时机有两BEFORE和AFTER,可以在触发事件之前也可以在触发事件之后发生。
触发器用途
DML触发器触发事件:
- INSERT:当往表或 视图中插入一行时触发
- UPDATE:当在表或 视图中更新一行时触发
- DELETE:当在表或 视图中删除一行时触发
关联触发器
把一个数据库触发器的动作与另一个触发器联系起来,使之触发另一个触发器。
触发器语法
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF} triggering_event
referencing_clause
[WHEN trigger_condition]
[FOR EACH ROW]
trigger_body;
其中:
使用触发器约束
查看触发器脚本:
select text
from user_source
where TYPE='TRIGGER';
DML触发器的组成与类型
大体结构
DML触发器组成:
触发时间:
行级触发器和语句级触发器区别:
主要在于影响次数不同:
具体应根据进行一个操作时触发器的触发次数,来决定是创建一个语句级还是行级触发器。
BEFORE 型语句级触发器:
CREATE OR REPLACE TRIGGER secure_emp
BEFORE INSERT ON emp
BEGIN
IF (TO_CHAR(SYSDATE,’DY’) in (‘SAT’,’SUN’))
OR (to_number(TO_CHAR(sysdate,’HH24’)) NOT BETWEEN 8 AND 18) THEN RAISE_APPLICATION_ERROR(-20500,’you may only insert into EMP during normal hours.’);
END IF;
END;
AFTER型语句级触发器
Create or replace TRIGGER check_sal_count
AFTER UPDATE OF sal ON emp
Declare
V_sal_changes NUMBER;
V_max_changes NUMBER;
Begin
SELECT upd,max_upd INTO v_sal_changes,v_max_changes FROM audit_able WHERE user_name=user AND table_name=‘EMP’ AND column_name=‘SAL’;
IF v_sal_changges>v_max_changes THEN
RAISE_APPLICATION_ERROR(-20501,’you may only make a maximum of’|| TO_CHAR(v_max_chages)||’to the sal column’);
End if;
End;
DML行级触发器:
CREATE OR REPLACE TRIGGER cascade_update
AFTER UPDATE OR DELETE ON dept
FOR EACH ROW
BEGIN
UPDATE emp
SET emp.deptno=:new.deptno
WHERE emp.deptno=:old.deptno;
END;
行级和语句级触发器在于执行的次数不同,我们可以根据需求来实现不同的触发器。
但是,可能会出现在一个触发器中既有删除、更新又有插入时,如何判断触发器是因哪个时间而动作?
在触发器体中使用谓词(INSERTING、UPDATING或DELETING)判断是哪个触发事件触发了触发器,从而把多种触发事件组成一个触发器。
比如:
CREATE OR REPLACE TRIGGER emp_infor
BEFORE INSERT OR DELTE OR UPDATE ON employee
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO emp_infor(emp_id,emp_name,emp_job,emp_sal)
VALUES (upper(:NEW.empno),:new.ename,:new.job,:new. sal);
END IF;
END;
在触发器正在处理过程中我们用:old和:new 两个相关标识符来访问行级触发器中的行数据。
使用“:old”和“:new”应注意的问题:
Create or replace trigger audit_emp_values
AFTER DELETE OR INSERT OR UPDATE ON emp
FOR EACH ROW
BEGIN
INSERT INTO autit_emp_values(user_name,timestamp,empno,old_ename,new_ename,old_job,new_job,old_mgr,new_mgrold_sal,new_sal)
VALUES(USER,SYSDATE,:old.empno,:old.ename,:new.ename,;old.job,:new.job,:old.mgr,:new.mgr,:old.sal,:new.sal);
END;
用WHEN子句来保证,当某些行满足一定条件时,在该行上行级触发器才被触发。WHEN后面是一个布尔表达式,它将对每一行进行求值。
Create or replace trigger derive_comm
BEFORE INSERT OR UPDATE OF sal ON emp
FOR EACH ROW
WHEN (new.job=‘SALESMAN’)
BEGIN
:new.comm :=:old.comm*(:new.sal/:old.sal)
END;
注意:在when条件中,new前面不加冒号。
起别名:
CREATE OR REPLACE TRIGGER audit_org_trig
AFTER INSERT ON emp
REFERENCING NEW AS new_org
FOR EACH ROW
WHEN (new_org.job < > 'MANAGER')//起别名 名字为new_org
BEGIN
UPDATE bonus
SET comm = :new_org.comm * 1.1
WHERE ename=:new_org.ename;
IF (SQL%NOTFOUND) THEN
INSERT INTO bonus VALUES (:new_org.ename,
:new_org.job,:new_org.job,:new_org.comm);
END IF;
END;
用REFERENCING子句来引用触发器语句体中数据
应用:审计
首先,创建一个表:
CREATE TABLE changes
(modify_empno NUMBER(4) NOT NULL,
for_new VARCHAR2(10),
For_old VARCHAR2(10),
Changed_date DATE DEFAULT sysdate,
changed_by VARCHAR2(30) DEFAULT USER);
然后创建一个触发器,在更新emp之后把数据插入changes表中。
CREATE OR REPLACE TRIGGER ref_audit
AFTER UPDATE ON emp
REFERENCING NEW AS current_new OLD AS current_old
FOR EACH ROW
BEGIN
INSERT INTO changes VALUES (:current_new.empno,
:current_new.sal,:current_old.sal,sysdate,user);
END ref_audit;
触发器管理
当一个触发器不再适宜被触发时,可以使其处于无效状态。即关闭触发器。关闭触发器与删除触发器是有区别的。删除触发器是从数据字典中永久删除,而关闭触发器只是让触发器失效,暂时不能被触发,并没有被从数据字典中删除,在需要时可再让其生效。
语法格式:
ALTER TRIGGER 触发器名 {DISABLE | ENABLE}
其中:
DISABLE 是关闭触发器
ENABLE是打开触发器
如果想要查看触发器的状态,可以查看数据字典:
USER_TRIGGERS
SELECT * from USER_TRIGGERS
在DML触发器中进行的数据操作
可以修改没有关联的表的数据,对与触发器没有关联的表的修改没有限制
例如:修改一个与触发器没有关联的表,为EMP表中的所有数据做一个历史记录(即审计)
CREATE OR REPLACE TRIGGER audit_emp_values
AFTER DELETE OR INSERT OR UPDATE ON emp
FOR EACH ROW
BEGIN
INSERT INTO autit_emp_values(user_name,timestamp,empno,old_ename,new_ename,old_job,new_job,ols_mgr,new_mgrold_sal,new_sal)
VALUES(USER,SYSDATE,:old.empno,:old.ename,:new.ename,;old.job,:new.job,:old,mgr,:new.mgr,:old.sal,:new.sal);
END;
用:old和:new 可以用来应用在审计上。
可以读取一个没有变化的表的数据
例如:
强行在雇员的薪水和其工作之间设置一个复杂的完整性限制,使其薪水不能超过其工作的所允许的薪水范围。从工资等级表中取出某一个工作的工资范围,工资登记表是一个没有变化的表。
Create or replace trigger check_sal
BEFORE INSERT OR UPDATE OF sal ON emp
FOR EACH ROW
WHEN (new.job<>'PRESIDENT')
DECLARE
v_minsal sal_guide.minsal%TYPE;
v_maxsal sal_guide.maxsal%TYPE;
BEGIN
SELECT minsal,maxsal INTO v_minsal,v_maxsal
FROM sal_guide WHERE job=:new.job;
IF :new.sal< v_minsal OR :new.sal > v_maxsal
THEN
RAISE_APPLICATION_ERROR(-20505,'salary'|| TO_CHAR(:new.sal)||'is out of range for employee'||TO_CHAR(:new.empno));
END IF;
END;
不允许从一个变化的表和被触发的表中读取数据
SQL>grant select, insert,update,delete on emp to clerk;
应用2、审计
(1)通过系统服务器进行审计
- 可审计用户查询、数据操纵及数据定义的次数。
- 把审计情况写入集中的审计表中(数据字典)。
- 每次启动或每次访问时在审计表中增加新的一行。
- 捕获成功和失败的操作。
(2)利用触发器进行审计
- 只审计数据操纵语句,不审计查询操作和数据定义语句。审计操纵语句所操作的值。
-将审计情况写入用户定义的审计表。
- 在每次启动或访问时审计表中增加新的行。
- 只审计成功的操作
应用3、数据完整性
例如:用触发器实现数据的完整性。确保薪水不能降低,但同时增长幅度不能超过10%。
CREATE OR REPLACE TRIGGER check_sal
BEFORE UPDATE OF sal ON emp
FOR EACH ROW
WHEN (new.salOR new.sal>old.sal*1.1)
BEGIN
RAISE_APPLICATION_ERROR(-20508,’do not decrease salary nor increase by more than 10%.’);
END;