一 创建DML 触发器
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 。
:NEW 修饰符表示操作完成后列的值
:OLD 修饰符表示操作完成前列的值
:NEW和:OLD的具体使用见表9-1。
表9-1 :NEW和:OLD的值
特性 INSERT UPDATE DELETE
OLD NULL 实际值 实际值
NEW 实际值 实际值 NULL
【例9-1】建立职工表emp的日志表EMPPLOYEES_LOG。当职工表进行DML操作时候,把职工表具体的操作名称,时间等信息插入到日志表中。
具体代码如下:
–第九章\sfq.sql
–建立职工表
create table emp
(empno NUMBER(4), --职工号
job VARCHAR2(10), --岗位
sal NUMBER(7,2) --薪水
);
–往职工表中插入数据
insert into EMP
select ‘1105’,‘工程师’,6500 from dual;
Insert into EMP
select ‘1135’,‘质检员’,3000 from dual;
Commit;
–建立日志表
create table EMPLOYEES_LOG
(
WHO VARCHAR2(30),
WHEN DATE,
ACTION VARCHAR2(50),
TALENME VARCHAR2(30)
)
CREATE OR REPLACE Trigger biud_employee_copy
Before insert or update or delete
On emp
Declare
tn VARCHAR2(10);
Begin
tn:=‘员工表’;
– :GLOBAL.USERNAME:=‘hzm’
if inserting then
Insert into employees_log(
Who,when,TALENME,action)
Values(user, sysdate,tn,‘插入新数据’);
END IF;
if updating then
Insert into employees_log(
Who,when,TALENME,action)
Values( user, sysdate,tn,‘更新数据’);
END IF;
if deleting then
Insert into employees_log(
Who,when,TALENME,action)
Values( user, sysdate,tn,‘删除数据’);
END IF;
End;
–插入数据
insert into EMP
select ‘1237’,‘项目经理’,8000 from dual;
–删除数据
Delete from EMP where empno=‘1135’;
Commit;
Select * from emp;
Select * from EMPLOYEES_LOG;
查询emp 表数据,结果如图9-1所示。
图9-1查询职工表的数据。
查询日志表数据,结果如图9-2所示。
图9-2查询日志表的数据。
【例9-2】建立职工表emp和审查表audit_emp_values,在职工表上建立一个触发器,当该表进行DML 操作时候,记录表中的旧值和新值,然后插入审查表。
具体代码如下:
– 第九章\sfq.sql
–建立职工表
create table emp
(empno NUMBER(4),
job VARCHAR2(10),
sal NUMBER(7,2)
)
–建立审查表
CREATE TABLE audit_emp_values
(user_name VARCHAR2(50),
timestamp DATE,
id NUMBER(4),
old_last_name VARCHAR2(10),
new_last_name VARCHAR2(10),
old_title VARCHAR2(10),
new_title VARCHAR2(10),
old_salary NUMBER(7,2),
new_salary NUMBER(7,2));
–建立行级触发器
CREATE OR REPLACE TRIGGER audit_emp_values
AFTER
DELETE OR INSERT OR UPDATE ON emp
FOR EACH ROW
BEGIN
INSERT INTO audit_emp_values (user_name,
timestamp, id,
old_title, new_title, old_salary, new_salary)
VALUES (USER, SYSDATE, :old.empno,
:old.job, :new.job, :old.sal, :new.sal);
END;
----职工表执行dml 操作
insert into emp (empno,job,sal) values(9,‘worker’,3000);
insert into emp (empno,job,sal) values(8,‘hunter’,100);
Commit;
Update emp set sal=4500 where empno=‘8’ ;
Commit;
delete from emp where job=‘hunter’;
Select * from emp;
Select * from audit_emp_values;
查询职工表,结果如图9-3所示。
图9-3执行DML操作后查询职工表的数据。
查询审查表,结果如图9-4所示。
图9-4执行DML操作后查询职工表的数据
三创建INSTEAD OF 触发器
具体格式如下:
CREATE [OR REPLACE] TRIGGER trigger_name
INSTEAD OF
{INSERT | DELETE | UPDATE [OF column [, column …]]}
[OR {INSERT | DELETE | UPDATE [OF column [, column …]]}…]
ON [schema.] view_name --只能定义在视图上
[REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
[FOR EACH ROW ] --因为INSTEAD OF触发器只能在行级上触发,所以没有必要指定
[WHEN condition]
PL/SQL_block | CALL procedure_name;
INSTEAD OF 选项使Oracle激活触发器,而不执行触发事件。只能对视图和对象视图建立INSTEAD OF触发器,而不能对表、模式和数据库建立INSTEAD OF 触发器。
【例9-3】建立视图emp_view,取表emp记录数和总工资数。建立INSTEAD OF
触发器,当删除表的数据后,可以删除视图的的数据,最后查询删除数据后视图的数据。
建立触发器,代码如下:
– 第九章\sfq.sql
–建 emp表,插入记录
create table emp
(empno NUMBER(4),
job VARCHAR2(10),
sal NUMBER(7,2)
);
insert into emp (empno,job,sal) values(9,‘worker’,3000);
Commit;
–建立视图,统计职工表的总职工数和总工资
CREATE OR REPLACE VIEW emp_view AS
SELECT empno, count(*) total_employeer, sum(sal) total_salary
FROM emp GROUP BY empno;
–建立触发器,当删除职工表数据时候触发
CREATE OR REPLACE TRIGGER emp_view_delete
INSTEAD OF DELETE ON emp_view FOR EACH ROW
BEGIN
DELETE FROM emp WHERE empno= :old.empno;
END emp_view_delete;
–删除视图数据
DELETE FROM emp_view WHERE empno=9;
Commit;
–查询视图的数据
Select * from emp_view
查询视图,结果如图9-5所示
图9-5查询视图emp_view的数据
四 创建系统事件触发器
Oracle 10G提供的系统事件触发器可以在DDL或数据库系统上被触发。DDL指的是数据定义语言,如CREATE 、ALTER及DROP 等。而数据库系统事件包括数据库服务器的启动或关闭、用户的登录与退出、数据库服务错误等。创建系统触发器的语法如下:
CREATE OR REPLACE TRIGGER [sachema.]trigger_name
{BEFORE|AFTER}
{ddl_event_list | database_event_list}
ON { DATABASE | [schema.]SCHEMA }
[WHEN condition]
PL/SQL_block | CALL procedure_name;
dl_event_list:一个或多个DDL 事件,多个事件中间用 OR 分开。
database_event_list:一个或多个数据库事件,多个事件中间间用 OR 分开。
系统事件触发器既可以建立在一个模式上,又可以建立在整个数据库上。当建立在模式(SCHEMA)之上时,只有模式所指定用户的DDL操作和它们所导致的错误才激活触发器, 默认时为当前用户模式。当建立在数据库(DATABASE)之上时,该数据库所有用户的DDL操作和他们所导致的错误,以及数据库的启动和关闭均可激活触发器。要在数据库之上建立触发器时,要求用户具有ADMINISTER DATABASE TRIGGER权限。
系统触发器的种类和事件出现的时机(前或后),见表9-2。
表9-2 系统时间允许的时机
事件 允许的时机 说明
STARTUP AFTER 启动数据库实例之后触发
SHUTDOWN BEFORE 关闭数据库实例之前触发(非正常关闭不触发)
SERVERERROR AFTER 数据库服务器发生错误之后触发
LOGON AFTER 成功登录连接到数据库后触发
LOGOFF BEFORE 开始断开数据库连接之前触发
CREATE BEFORE
AFTER 在执行CREATE语句创建数据库对象之前、之后触发
DROP BEFORE
AFTER 在执行DROP语句删除数据库对象之前、之后触发
ALTER BEFORE
AFTER 在执行ALTER语句更新数据库对象之前、之后触发
DDL BEFORE
AFTER 在执行大多数DDL语句之前、之后触发
GRANT BEFORE
AFTER 执行GRANT语句授予权限之前、之后触发
REVOKE BEFORE
AFTER 执行REVOKE语句收权限之前、之后触犯发
RENAME BEFORE
AFTER 执行RENAME语句更改数据库对象名称之前、之后触犯发
AUDIT / NOAUDIT BEFORE
AFTER 执行AUDIT或NOAUDIT进行审计或停止审计之前、之后触发
【例9-4】建立事件表ddl_event ,当数据库有DML操作,把具体事件名称和时间插入事件表中。
建立事件表,代码如下:
–第九章\sfq.sql
create table ddl_event
(crt_date timestamp PRIMARY KEY,
event_name VARCHAR2(20),
user_name VARCHAR2(10),
obj_type VARCHAR2(20),
obj_name VARCHAR2(20));
建立触发器,当发生ddl操作,把时间名称等插入事件表,代码如下:
CREATE OR REPLACE TRIGGER tr_ddl
AFTER DDL ON SCHEMA
BEGIN
INSERT INTO ddl_event VALUES
(systimestamp,ora_sysevent, ora_login_user,
ora_dict_obj_type, ora_dict_obj_name);
END tr_ddl;
【例9-5】建立登录事件表log_event ,当数据库有登录或退出操作,把具体事件名称和用户等信息插入时间表中。
具体代码如下:
–第九章\sfq.sql
CREATE TABLE log_event
(user_name VARCHAR2(10),
address VARCHAR2(20),
logon_date timestamp,
logoff_date timestamp);
–创建登录触发器
CREATE OR REPLACE TRIGGER tr_logon
AFTER LOGON ON DATABASE
BEGIN
INSERT INTO log_event (user_name, address, logon_date)
VALUES (ora_login_user, ora_client_ip_address, systimestamp);
END tr_logon;
–创建退出触发器
CREATE OR REPLACE TRIGGER tr_logoff
BEFORE LOGOFF ON DATABASE
BEGIN
INSERT INTO log_event (user_name, address, logoff_date)
VALUES (ora_login_user, ora_client_ip_address, systimestamp);
END tr_logoff;
9.5触发器的编译和删除
1)重新编译触发器
如果在触发器内调用其它函数或过程,当这些函数或过程被删除或修改后,触发器的状态将被标识为无效。当DML语句激活一个无效触发器时,Oracle将重新编译触发器代码,如果编译时发现错误,这将导致DML语句执行失败。
在PL/SQL程序中可以调用ALTER TRIGGER语句重新编译已经创建的触发器,格式如下:
ALTER TRIGGER [schema.] trigger_name COMPILE [ DEBUG]
其中:DEBUG 选项要器编译器生成PL/SQL 程序条使其所使用的调试代码。
2) 启用触发器的格式如下:
ALTER TRIGGER trigger_name ENABLE;
3)禁用触发器的格式如下:
alter trigger trigger_name disable;
4)删除触发器格式如下:
DROP TRIGGER trigger_name;
当删除其他用户模式中的触发器名称,需要具有DROP ANY TRIGGER系统权限,当删除建立在数据库上的触发器时,用户需要具有ADMINISTER DATABASE TRIGGER系统权限。此外,当删除表或视图时,建立在这些对象上的触发器也随之删除。
触发器优点:
强化约束:强制复杂业务的规则和要求,能实现比check语句更为复杂的约束。
跟踪变化:触发器可以侦测数据库内的操作,从而禁止数据库中未经许可的更新和变化。
级联运行:侦测数据库内的操作时,可自动地级联影响整个数据库的各项内容。
嵌套调用:触发器可以调用一个或多个存储过程。触发器最多可以嵌套32层。
缺点:性能较低。因为在运行触发器时,系统处理的大部分时间花费在参照其他表的处理上,这些表既不在内存中也不在数据库设备上,而删除表和插入表总是位于内存中。