oracle 触发器实例
8.1 触发器类型8.1.1 DML触发器8.1.2 替代触发器8.1.3 系统触发器8.2 创建触发器8.2.1 触发器触发次序8.2.2 创建DML触发器8.2.3 创建替代(INSTEAD OF)触发器8.2.3 创建系统事件触发器8.2.4 系统触发器事件属性8.2.5 使用触发器谓词8.2.6 重新编译触发器8.3 删除和使能触发器 8.4 触发器和数据字典8.5 数据库触发器的应用举例
触发器是许多关系数据库系统都提供的一项技术。在ORACLE系统里,触发器类似过程和函数,都有声明,执行和异常处理过程的PL/SQL块。
8.1 触发器类型
触发器在数据库里以独立的对象存储,它与存储过程和函数不同的是,存储过程与函数需要用户显示调用才执行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。所以运行触发器就叫触发或点火(firing)。ORACLE事件指的是对数据库的表进行的INSERT、UPDATE及DELETE操作或对视图进行类似的操作。ORACLE将触发器的功能扩展到了触发ORACLE,如数据库的启动与关闭等。所以触发器常用来完成由数据库的完整性约束难以完成的复杂业务规则的约束,或用来监视对数据库的各种操作,实现审计的功能。
8.1.1 DML触发器
ORACLE可以在DML语句进行触发,可以在DML操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
8.1.2 替代触发器
由于在ORACLE里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。它就是ORACLE 8专门为进行视图操作的一种处理方法。
8.1.3 系统触发器
ORACLE 8i 提供了第三种类型的触发器叫系统触发器。它可以在ORACLE数据库系统的事件中进行触发,如ORACLE系统的启动与关闭等。
触发器组成:
触发事件:引起触发器被触发的事件。 例如:DML语句(INSERT, UPDATE, DELETE语句对表或视图执行数据处理操作)、DDL语句(如CREATE、ALTER、DROP语句在数据库中创建、修改、删除模式对象)、数据库系统事件(如系统启动或退出、异常错误)、用户事件(如登录或退出数据库)。
触发时间:即该TRIGGER 是在触发事件发生之前(BEFORE)还是之后(AFTER)触发,也就是触发事件和该TRIGGER 的操作顺序。 触发操作:即该TRIGGER 被触发之后的目的和意图,正是触发器本身要做的事情。 例如:PL/SQL 块。 触发对象:包括表、视图、模式、数据库。只有在这些对象上发生了符合触发条件的触发事件,才会执行触发操作。 触发条件:由WHEN子句指定一个逻辑表达式。只有当该表达式的值为TRUE时,遇到触发事件才会自动执行触发器,使其执行触发操作。 触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器。 语句级(STATEMENT)触发器:是指当某触发事件发生时,该触发器只执行一次; 行级(ROW)触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。
编写触发器时,需要注意以下几点:
触发器不接受参数 一个表上最多可有12个触发器,但同一时间、同一事件、同一类型的触发器只能有一个。并各触发器之间不能有矛盾。 在一个表上的触发器越多,对在该表上的DML操作的性能影响就越大。 触发器最大为32KB。若确实需要,可以先建立过程,然后在触发器中用CALL语句进行调用。 在触发器的执行部分只能用DML语句(SELECT、INSERT、UPDATE、DELETE),不能使用DDL语句(CREATE、ALTER、DROP)。 触发器中不能包含事务控制语句(COMMIT,ROLLBACK,SAVEPOINT)。因为触发器是触发语句的一部分,触发语句被提交、回退时,触发器也被提交、回退了。 在触发器主体中调用的任何过程、函数,都不能使用事务控制语句。 在触发器主体中不能申明任何Long和blob变量。新值new和旧值old也不能向表中的任何long和blob列。不同类型的触发器(如DML触发器、INSTEAD OF触发器、系统触发器)的语法格式和作用有较大区别。
8.2 创建触发器
创建触发器的一般语法是:
ld.deptno,ld.empno,
ld.ename ,
ld.job,:old.mgr,
ld.sal,
ld.comm,
ld.hiredate );
END;
DELETE emp WHERE empno=7788;
DROP TABLE emp_his;
DROP TRIGGER del_emp;
例2:限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围,即不允许在非工作时间修改departments表。
ld.salary THEN
RAISE_APPLICATION_ERROR(-20001, '部门80的人员的工资不能降');
END IF;
WHEN UPDATING ('commission_pct') THEN
IF :NEW.commission_pct <ld.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。
ld.region_id;
例5:在触发器中调用过程。
ld.hire_date, sysdate,
ld.job_id,
ld.department_id);
END;
8.2.3 创建替代(INSTEAD OF)触发器
创建触发器的一般语法是:
ld.deptno;
END emp_view_delete;
DELETE FROM emp_view WHERE deptno=10;
DROP TRIGGER emp_view_delete;
DROP VIEW emp_view;
例2:创建复杂视图,针对INSERT操作创建INSTEAD OF触发器,向复杂视图插入数据。
创建视图:
AS
SELECT r.region_id,
r.region_name,
c.country_id,
c.country_name
FROM regions r,
countries c
WHERE r.region_id = c.region_id;
创建触发器:
INSERT ON v_reg_cou FOR EACH ROW DECLARE v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM regions WHERE region_id = :new.r_id;
IF v_count = 0 THEN
INSERT INTO regions
(region_id, region_name
) VALUES
(:new.r_id, :new.r_name
);
END IF;
SELECT COUNT(*) INTO v_count FROM countries WHERE country_id = :new.c_id;
IF v_count = 0 THEN
INSERT
INTO countries
(
country_id,
country_name,
region_id
)
VALUES
(
:new.c_id,
:new.c_name,
:new.r_id
);
END IF;
END;
创建INSTEAD OF触发器需要注意以下几点:
只能被创建在视图上,并且该视图没有指定WITH CHECK OPTION选项。 不能指定BEFORE 或 AFTER选项。 FOR EACH ROW子可是可选的,即INSTEAD OF触发器只能在行级上触发、或只能是行级触发器,没有必要指定。 没有必要在针对一个表的视图上创建INSTEAD OF触发器,只要创建DML触发器就可以了。
8.2.3 创建系统事件触发器
ORACLE10G提供的系统事件触发器可以在DDL或数据库系统上被触发。DDL指的是数据定义语言,如CREATE 、ALTER及DROP 等。而数据库系统事件包括数据库服务器的启动或关闭,用户的登录与退出、数据库服务错误等。创建系统触发器的语法如下:
创建触发器的一般语法是:
例1:创建触发器,存放有关事件信息。
8.2.6 重新编译触发器
如果在触发器内调用其它函数或过程,当这些函数或过程被删除或修改后,触发器的状态将被标识为无效。当DML语句激活一个无效触发器时,ORACLE将重新编译触发器代码,如果编译时发现错误,这将导致DML语句执行失败。
在PL/SQL程序中可以调用ALTER TRIGGER语句重新编译已经创建的触发器,格式为:
ld_emp.sal);
DBMS_OUTPUT.PUT_LINE(''sal 新值:''|| :new_emp.sal);
UPDATE dept_summary
SET sal_sum=sal_sum + :new_emp.sal -ld_emp.sal
WHERE deptno = :new_emp.deptno;
END;'
);
DBMS_UTILITY.EXEC_DDL_STATEMENT(
'CREATE OR REPLACE TRIGGER trig2_insert
AFTER INSERT ON emp
REFERENCING NEW AS new_emp
FOR EACH ROW
DECLARE
I NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(''正在执行trig2_insert 触发器…'');
SELECT COUNT(*) INTO I
FROM dept_summary WHERE deptno = :new_emp.deptno;
IF I > 0 THEN
UPDATE dept_summary
SET sal_sum=sal_sum+:new_emp.sal,
Emp_count=emp_count+1
WHERE deptno = :new_emp.deptno;
ELSE
INSERT INTO dept_summary
VALUES (:new_emp.deptno, :new_emp.sal, 1);
END IF;
END;'
);
DBMS_UTILITY.EXEC_DDL_STATEMENT(
'CREATE OR REPLACE TRIGGER trig2_delete
AFTER DELETE ON emp
REFERENCING OLD AS old_emp
FOR EACH ROW
DECLARE
I NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(''正在执行trig2_delete 触发器…'');
SELECT emp_count INTO I
FROM dept_summary WHERE deptno =ld_emp.deptno;
IF I >1 THEN
UPDATE dept_summary
SET sal_sum=sal_sum -ld_emp.sal,
Emp_count=emp_count - 1
WHERE deptno =ld_emp.deptno;
ELSE
DELETE FROM dept_summary WHERE deptno =ld_emp.deptno;
END IF;
END;'
);
INSERT INTO dept(deptno, dname, loc)
VALUES(90, 'demo_dept', 'none_loc');
INSERT INTO emp(ename, deptno, empno, sal)
VALUES(USER, 90, 9999, 3000);
INSERT INTO emp(ename, deptno, empno, sal)
VALUES(USER, 90, 9998, 2000);
DBMS_OUTPUT.PUT_LINE('插入后');
Disp_dept_summary();
UPDATE emp SET sal = sal*1.1 WHERE deptno=90;
DBMS_OUTPUT.PUT_LINE('修改后');
Disp_dept_summary();
DELETE FROM emp WHERE deptno=90;
DELETE FROM dept WHERE deptno=90;
DBMS_OUTPUT.PUT_LINE('删除后');
Disp_dept_summary();
DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP TRIGGER trig2_update');
DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP TRIGGER trig2_insert');
DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP TRIGGER trig2_delete');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;
例3:利用ORACLE提供的条件谓词INSERTING、UPDATING和DELETING创建与例2具有相同功能的触发器。
ld_emp.sal != :new_emp.sal THEN
DBMS_OUTPUT.PUT_LINE(''正在执行trig2 触发器…'');
DBMS_OUTPUT.PUT_LINE(''sal 旧值:''||ld_emp.sal);
DBMS_OUTPUT.PUT_LINE(''sal 新值:''|| :new_emp.sal);
UPDATE dept_summary
SET sal_sum=sal_sum + :new_emp.sal -ld_emp.sal
WHERE deptno = :new_emp.deptno;
ELSIF INSERTING THEN
DBMS_OUTPUT.PUT_LINE(''正在执行trig2触发器…'');
SELECT COUNT(*) INTO I
FROM dept_summary
WHERE deptno = :new_emp.deptno;
IF I > 0 THEN
UPDATE dept_summary
SET sal_sum=sal_sum+:new_emp.sal,
Emp_count=emp_count+1
WHERE deptno = :new_emp.deptno;
ELSE
INSERT INTO dept_summary
VALUES (:new_emp.deptno, :new_emp.sal, 1);
END IF;
ELSE
DBMS_OUTPUT.PUT_LINE(''正在执行trig2触发器…'');
SELECT emp_count INTO I
FROM dept_summary WHERE deptno =ld_emp.deptno;
IF I > 1 THEN
UPDATE dept_summary
SET sal_sum=sal_sum -ld_emp.sal,
Emp_count=emp_count - 1
WHERE deptno =ld_emp.deptno;
ELSE
DELETE FROM dept_summary
WHERE deptno =ld_emp.deptno;
END IF;
END IF;
END;'
);
INSERT INTO dept(deptno, dname, loc)
VALUES(90, 'demo_dept', 'none_loc');
INSERT INTO emp(ename, deptno, empno, sal)
VALUES(USER, 90, 9999, 3000);
INSERT INTO emp(ename, deptno, empno, sal)
VALUES(USER, 90, 9998, 2000);
DBMS_OUTPUT.PUT_LINE('插入后');
Disp_dept_summary();
UPDATE emp SET sal = sal*1.1 WHERE deptno=90;
DBMS_OUTPUT.PUT_LINE('修改后');
Disp_dept_summary();
DELETE FROM emp WHERE deptno=90;
DELETE FROM dept WHERE deptno=90;
DBMS_OUTPUT.PUT_LINE('删除后');
Disp_dept_summary();
DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP TRIGGER trig2');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;
例4:创建INSTEAD OF 触发器。首先创建一个视图myview, 由于该视图是复合查询所产生的视图,所以不能执行DML语句。根据用户对视图所插入的数据判断需要将数据插入到哪个视图基表中,然后对该基表执行插入操作。
ld.empno);
ELSE
INSERT INTO audit_table
VALUES(audit_seq.NEXTVAL, user, time_now,
terminal, 'EMP', 'UPDATE',ld.empno);
IF UPDATING('SAL') THEN
INSERT INTO audit_table_val
VALUES(audit_seq.CURRVAL, 'SAL',ld.sal, :new.sal);
ELSE UPDATING('DEPTNO')
INSERT INTO audit_table_val
VALUES(audit_seq.CURRVAL, 'DEPTNO',ld.deptno, :new.deptno);
END IF;
END IF;
END;
增强数据的完整性管理;
例:修改DEPT表的DEPTNO列时,同时把EMP表中相应的DEPTNO也作相应的修改;
引用
CREATE SEQUENCE update_sequence
INCREMENT BY 1
START WITH 1000
MAXVALUE 5000 CYCLE;
ALTER TABLE emp
ADD update_id NUMBER;
CREATE OR REPLACE PACKAGE integritypackage AS
Updateseq NUMBER;
END integritypackage;
CREATE OR REPLACE PACKAGE BODY integritypackage AS
END integritypackage;
CREATE OR REPLACE TRIGGER dept_cascade1
BEFORE UPDATE OF deptno ON dept
DECLARE
Dummy NUMBER;
BEGIN
SELECT update_sequence.NEXTVAL INTO dummy FROM dual;
Integritypackage.updateseq:=dummy;
END;
CREATE OR REPLACE TRIGGER dept_cascade2
AFTER DELETE OR UPDATE OF deptno ON dept
FOR EACH ROW
BEGIN
IF UPDATING THEN
UPDATE emp SET deptno=:new.deptno,
update_id=integritypackage.updateseq
WHERE emp.deptno=:old.deptno AND update_id IS NULL;
END IF;
IF DELETING THEN
DELETE FROM emp
WHERE emp.deptno=:old.deptno;
END IF;
END;
CREATE OR REPLACE TRIGGER dept_cascade3
AFTER UPDATE OF deptno ON dept
BEGIN
UPDATE emp SET update_id=NULL
WHERE update_id=integritypackage.updateseq;
END;
SELECT * FROM EMP ORDER BY DEPTNO;
UPDATE dept SET deptno=25 WHERE deptno=20;
帮助实现安全控制;
例:保证对EMP表的修改仅在工作日的工作时间;
引用CREATE TABLE company_holidays(day DATE);
INSERT INTO company_holidays
VALUES(sysdate);
INSERT INTO company_holidays
VALUES(TO_DATE('21-10月-01', 'DD-MON-YY'));
CREATE OR REPLACE TRIGGER emp_permit_change
BEFORE INSERT OR DELETE OR UPDATE ON emp
DECLARE
Dummy NUMBER;
Not_on_weekends EXCEPTION;
Not_on_holidays EXCEPTION;
Not_working_hours EXCEPTION;
BEGIN
/* check for weekends */
IF TO_CHAR(SYSDATE, 'DAY') IN ('星期六', '星期日') THEN
RAISE not_on_weekends;
END IF;
/* check for company holidays */
SELECT COUNT(*) INTO dummy FROM company_holidays
WHERE TRUNC(day)=TRUNC(SYSDATE);
IF dummy >0 THEN
RAISE not_on_holidays;
END IF;
/* check for work hours(8:00 AM to 18:00 PM */
IF (TO_CHAR(SYSDATE,'HH24')<8 OR TO_CHAR(SYSDATE, 'HH24')>18) THEN
RAISE not_working_hours;
END IF;
EXCEPTION
WHEN not_on_weekends THEN
RAISE_APPLICATION_ERROR(-20324,
'May not change employee table during the weekends');
WHEN not_on_holidays THEN
RAISE_APPLICATION_ERROR(-20325,
'May not change employee table during a holiday');
WHEN not_working_hours THEN
RAISE_APPLICATION_ERROR(-20326,
'May not change employee table during no_working hours');
END;
管理复杂的表复制;
防止非法的事务发生; 自动生成派生的列值;帮助式显复杂的商业管理。
--------------------
http://www.cnblogs.com/huyong/archive/2011/04/27/2030466.html