数据库---DML触发器

DML触发器

触发器的基本概念

  • 触发器类似于函数和过程,它们都是具有声明部分、执行部分和异常处理部分的程序实体单元。
  • 触发器必须在数据库中以独立对象的身份存储。
  • 触发器是在事件发生时隐式地运行的,不能接收参数,不能被调用。
  • 运行触发器的方式叫做激发(firing)触发器,触发事件可以是对数据库表的DML(INSERT、UPDATE或DELETE)操作或某种视图的操作

触发事件包括INSERT、UPDATE或DELETE,触发时机有两BEFORE和AFTER,可以在触发事件之前也可以在触发事件之后发生。

触发器用途

  • 通过记录修改内容和修改者来审计表中的信息。
  • 在表内容发生变更时,自动通知其他程序采取相应的处理。
  • 在订阅发布环境下,发布有关各种事件的信息。
  • 维护在表创建阶段通过声明限制无法实现的复杂完整性限制。
  • 触发器主要用于促进引用的完整性
  • 只能在无法使用下列表级别限制时使用它们
    -NOT NULL
    -UNIQUE KEY
    -PRIMARY KEY
    -PRIMARY KEY
    -CHECK

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;

其中:

  • trigger_name是触发器的名称,
  • triggering_event说明了激发触发器的事件(也可能包括特殊的表或视图)
  • trigger_body是触发器的代码。
  • referencing_clause用来引用正在处于修改状态下的行中的数据,
  • 如果在WHEN子句中指定trigger_condition的话,则首先对该条件求值。触发器主体只有在该条件为真值时才运行。

使用触发器约束

  • SELECT语句不能触发触发器;
  • 触发器不能象存储过程或函数那样被显式地调用,只能由事件来触发;
  • 触发器可以调用数据库存储过程、函数和包。
  • 必须将适当的权限授予用户,用户才能CREATE,DROP, ENABLE或DISABLE触发器。触发器可被失效或使之有效。

查看触发器脚本:

select text 
from user_source 
where TYPE='TRIGGER'; 

DML触发器的组成与类型

大体结构

数据库---DML触发器_第1张图片

DML触发器组成:

数据库---DML触发器_第2张图片

触发时间:

数据库---DML触发器_第3张图片

触发顺序:
数据库---DML触发器_第4张图片

行级触发器和语句级触发器区别:
主要在于影响次数不同:

  • 如果该语句只影响一行,则语句级与行级触发效果相同。
  • 如果该语句影响多行,则行级触发的次数比语句级触发的次数多。
  • 具体应根据进行一个操作时触发器的触发次数,来决定是创建一个语句级还是行级触发器。

    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”应注意的问题:

  • 在BEFORE类型行级触发器和AFTER类型行级触发器中使用这些标识符。
  • 在语句级触发器中不要使用这些标识符。
  • 在PL/SQL语句或SQL语句中,这些标识符前加上冒号(:)来引用它们。
  • 在行级触发器的WHEN条件中使用该标识符时,前面不要加冒号(:).
  • 在BEFORE触发器中修改 “:new”,不能修改“:old”,在AFTER触发器中不能修改“:new”
    比如:在行级触发器中获取某列的新值和旧值,为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,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;

不允许从一个变化的表和被触发的表中读取数据

  • 变化表是指直接或间接地受触发时间影响的表
  • 与触发器发生连接的表本身是一个变化表
  • 通过DELETE CASCADE完整性约束与被触发表产生联系的表是变化表
    DML触发器的应用
    应用1、安全性
    用系统本身的服务控制实体的安全性在ORACLE中通过给用户授权完成对表中数据进行操作的安全性管理
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;

你可能感兴趣的:(数据库,数据库)