技术开发 频道

ORACLE PL/SQL编程之八:把触发器说透

  8.2.2 创建DML触发器

  触发器名与过程名和包的名字不一样,它是单独的名字空间,因而触发器名可以和表或过程有相同的名字,但在一个模式中触发器名不能相同。

  DML触发器的限制:

  CREATE TRIGGER语句文本的字符长度不能超过32KB;

  触发器体内的SELECT 语句只能为SELECT … INTO …结构,或者为定义游标所使用的SELECT 语句。

  触发器中不能使用数据库事务控制语句 COMMIT; ROLLBACK, SVAEPOINT 语句;

  由触发器所调用的过程或函数也不能使用数据库事务控制语句;

     触发器中不能使用LONG, LONG RAW 类型;

   触发器内可以参照LOB 类型列的列值,但不能通过 :NEW 修改LOB列中的数据;

  DML触发器基本要点

   触发时机:指定触发器的触发时间。如果指定为BEFORE,则表示在执行DML操作之前触发,以便防止某些错误操作发生或实现某些业务规则;如果指定为AFTER,则表示在执行DML操作之后触发,以便记录该操作或做某些事后处理。

  触发事件:引起触发器被触发的事件,即DML操作(INSERT、UPDATE、DELETE)。既可以是单个触发事件,也可以是多个触发事件的组合(只能使用OR逻辑组合,不能使用AND逻辑组合)。

   条件谓词:当在触发器中包含多个触发事件(INSERT、UPDATE、DELETE)的组合时,为了分别针对不同的事件进行不同的处理,需要使用ORACLE提供的如下条件谓词。

  1)。INSERTING:当触发事件是INSERT时,取值为TRUE,否则为FALSE。

  2)。UPDATING [(column_1,column_2,…,column_x)]:当触发事件是UPDATE 时,如果修改了column_x列,则取值为TRUE,否则为FALSE。其中column_x是可选的。

  3)。DELETING:当触发事件是DELETE时,则取值为TRUE,否则为FALSE。

  解发对象:指定触发器是创建在哪个表、视图上。

  触发类型:是语句级还是行级触发器。

  触发条件:由WHEN子句指定一个逻辑表达式,只允许在行级触发器上指定触发条件,指定UPDATING后面的列的列表。

  问题:当触发器被触发时,要使用被插入、更新或删除的记录中的列值,有时要使用操作前、 后列的值.

  实现: :NEW 修饰符访问操作完成后列的值

  :OLD 修饰符访问操作完成前列的值 

特性INSERTUPDATEDELETE
OLDNULL实际值实际值
NEW实际值实际值NULL



    例1: 建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去。

CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2;

  CREATE
OR REPLACE TRIGGER tr_del_emp

  BEFORE DELETE
--指定触发时机为删除操作前触发

  
ON scott.emp

  
FOR EACH ROW --说明创建的是行级触发器

  BEGIN

  
--将修改前数据插入到日志记录表 del_emp ,以供监督使用。

  INSERT INTO emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate )

  VALUES( :old.deptno, :old.empno, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate );

  
END;

  DELETE emp WHERE empno
=7788;

  DROP TABLE emp_his;

  DROP TRIGGER del_emp;

  例2:限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围,即不允许在非工作时间修改departments表。

CREATE OR REPLACE TRIGGER tr_dept_time

  BEFORE INSERT
OR DELETE OR UPDATE

  
ON departments

  BEGIN

  
IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日')) OR (TO_CHAR(sysdate, 'HH24:MI') NOT BETWEEN '08:30' AND '18:00') THEN

  RAISE_APPLICATION_ERROR(
-20001, '不是上班时间,不能修改departments表');

  
END IF;

  
END;

  例3:限定只对部门号为80的记录进行行触发器操作。

CREATE OR REPLACE TRIGGER tr_emp_sal_comm

  BEFORE UPDATE OF salary, commission_pct

  
OR DELETE

  
ON HR.employees

  
FOR EACH ROW

  WHEN (old.department_id
= 80)

  BEGIN

  
CASE

  WHEN UPDATING (
'salary') THEN

  
IF :NEW.salary < :old.salary THEN

  RAISE_APPLICATION_ERROR(
-20001, '部门80的人员的工资不能降');

  
END IF;

  WHEN UPDATING (
'commission_pct') THEN

  
IF :NEW.commission_pct < :old.commission_pct THEN

  RAISE_APPLICATION_ERROR(
-20002, '部门80的人员的奖金不能降');

  
END IF;

  WHEN DELETING
THEN

  RAISE_APPLICATION_ERROR(
-20003, '不能删除部门80的人员记录');

  
END CASE;

  
END;

  
/*

  实例:

  UPDATE employees
SET salary = 8000 WHERE employee_id = 177;

  DELETE FROM employees WHERE employee_id in (
177,170);

  
*/

  例4:利用行触发器实现级联更新。在修改了主表regions中的region_id之后(AFTER),级联的、自动的更新子表countries表中原来在该地区的国家的region_id。

 CREATE OR REPLACE TRIGGER tr_reg_cou

  AFTER update OF region_id

  
ON regions

  
FOR EACH ROW

  BEGIN

  DBMS_OUTPUT.PUT_LINE(
'旧的region_id值是'||:old.region_id

  ||
'、新的region_id值是'||:new.region_id);

  UPDATE countries
SET region_id = :new.region_id

  WHERE region_id
= :old.region_id;

  
END;

        例5:在触发器中调用过程。

 

 CREATE OR REPLACE PROCEDURE add_job_history

  ( p_emp_id job_history.employee_id%type

  , p_start_date job_history.start_date%type

  , p_end_date job_history.end_date%type

  , p_job_id job_history.job_id%type

  , p_department_id job_history.department_id%type

  )

  
IS

  BEGIN

  INSERT INTO job_history (employee_id, start_date, end_date,

  job_id, department_id)

  VALUES(p_emp_id, p_start_date, p_end_date, p_job_id, p_department_id);

  
END add_job_history;

  
--创建触发器调用存储过程...

  CREATE
OR REPLACE TRIGGER update_job_history

  AFTER UPDATE OF job_id, department_id
ON employees

  
FOR EACH ROW

  BEGIN

  add_job_history(:old.employee_id, :old.hire_date, sysdate,

  :old.job_id, :old.department_id);

  
END;


0
相关文章