Oracle Trigger 触发器

Oracle Trigger


在Oracle8i之前,只允许给予表或者视图的的DML的操作,而从Oracle8i开始,不仅可以支持DML触发器,也允许给予系统事件和DDL的操作.


一、触发器的构成
触发器由触发事件、触发条件、触发操作三部分构成。


1、触发事件
指引起触发的sql语句、数据库事件或用户事件
包括:DML操作、启动关闭例程、oracle错误消息、用户登录断开会话、DML操作、DDL操作


2、触发条件
可选项,指使用when子句指定一个boolean表达式,当为true时,执行触发器相应的代码。


3、触发操作
指包含sql语句和其它执行代码的pl/sql块,不仅可以用pl/sql开发,也可以用java和c开发。
当触发条件为true时,会触发相应的代码,编写时需要注意以下几点:
1)、触发器代码大小不能超过32K,如果需要使用大量代码,应该建立存储过程,在触发器使用里调用。
2)、触发器只能包含select、insert、update、delete语句,不能包含ddl和事务控制语句。




二、触发器的分类
1.DML触发器(语句触发器、行级触发器)
2.事件触发器(系统事件触发器、客户事件触发器、DDL触发器)
3.替代触发器




三、DML触发器中,语句触发器与行级触发器的区别
1、行触发器有for each row子句,语句触发器没有for each row 子句。
2、行触发器,可以有when作为触发限制,可以使用new/old。语句触发器不能有when作为触发限制。
3、行触发器:对应DML语句所影响到的表中的每一行,触发器都要执行一遍。
4、语句触发:对应DML语句所影响到的表中的所有行,触发器只执行一遍。




四、DML触发器:指当执行DML语句时被隐含执行的触发器。


1、DML触发器应该具备得要素
建立DML触发器需要指定触发时机(before\after)、触发事件(insert\update\delete)、表名、触发器类型、触发器条件、触发操作。


1)、触发时机
before,在执行dml操作之前
after,在执行dml操作之后


2)、触发事件
用于指定导致触发器执行的DML操作,也即是insert,update,delete,触发事件可以单个可以多个。


3)、表名
DML触发器是针对特定表进行的,必须指定DMK操作对应的表。


4)、触发器类型
用于指定当触发事件发生之后,需要执行几次触发操作。语句触发器(默认)只执行一次,行触发器则每作用一行执行一次。


5)、触发条件
当为true时,执行触发器相应的代码,DML触发器只允许在行触发器上指定触发条件。


6)、触发操作
用于指定触发器执行代码。如果使用PL/SQL存储过程、JAVA存储过程或外部存储过程实现。
那么在触发操作部分可直接使用call语句调用。如果使用PL/SQL匿名块,则按照
declare
--定义变量
begin
--编写pl/sql  sql语句
exception
--异常处理
end;


2、DML触发器的触发顺序
1)、在单行数据上的触发顺序。
当针对同一表的相同DML操作而建立了多个DML触发器时,则触发器的顺序为
BEFORE语句触发器->BEFORE行级触发器->DML操作\->after行级触发器->after语句触发器。


2)、在多行数据上的触发顺序。
当针对某一表的相同DML操作而建立了多个DML触发器时,则触发器的顺序为
BEFORE语句触发器、
BEFORE行触发器
AFTER行触发器
BEFORE行触发器
AFTER行触发器
AFTER语句触发器


3、语句触发器


1)、语法
create or replace trigger trigger_name
timing event1 [or event2 or event2] on table_name
PL/SQL block;
其中timing指定触发时机(before/after),event1指定触发事件(insert\update\delete)


2)、before语句触发器
说明:为了确保DML操作在正常情况下执行。
create or replace trigger tri_tse
before insert or update or delete on emp
begin
if to_char(sysdate,'DY','nls_date_language=american') in ('SAT','SUN') then
  raise_application_error(-20001,'不能在休息日改变雇员信息');
end if;
end;


3)、after语句触发器
说明:用于审计DML操作,或在DML操作之后执行汇总运算。
举例:
create or replace trigger tri_tru
after update on emp
declare 
  sqltxt ora_name_list_t;
  vstmt varchar2(100);
  m binary_integer;
begin
  m:=ora_sql_txt(sqltxt);
  for i in 1..m loop
    vstmt:=vstmt || sqltxt(i);
  end loop;
  insert into ut_table(host,statement,exectime) values(sys_context('userenv','host'),vstmt,sysdate);
end;
--本触发器的作用将会记录更新的SQL语句插入到statemen字段中。


4)、使用条件谓词
当触发器中同时包含多个触发事件(insert\update\delete)时。为了在触发器代码中区分具体的触发事件
可以用下三个条件谓词:
inserting:代表insert语句触发事件
updating:代表update语句触发事件
deleting:代表delete语句触发事件


create or replace trigger tri_tse
before insert or update or delete on emp
begin
  if to_char(sysdate,'DY','nls_date_language=american') in ('SAT','SUN') then
    case when inserting then raise_application_error(-20001,'不能在休息日插入雇员信息');
    when updating then raise_application_error(-20001,'不能在休息日更新雇员信息');
    when deleting then raise_application_error(-20001,'不能在休息日删除雇员信息');
  end if;
end;


4、行级触发器
执行DML操作时,每作用一行就触发一次触发器。


1)、语法
create [or replace] trigger trigger_name
timing event1 [or event2 or event3] on table_name
[referencing old as old | new as new]
for each row
[when condition]
PL/SQL block;


其中timing指定触发时机(before/after),event1指定触发事件(insert\update\delete)
referencing子句用于指定引用新、旧数据的方式,for each row表示建立行触发器;when子句用于指定触发条件。


2)、before行级触发器
说明:确保数据符合商业逻辑或企业规范。
例子:
Create or replace trigger tr_emp_sal
before update of sal on emp
for each row
begin
  if :new.sal<:old.sal then
    raise_application_error(-20010,'工资只涨不降');
  end if;
end;


3)、after行级触发器
说明:为了审计数据的变化。
例子:
Create or replace trigger tr_sal_sal
after update of sal on emp
for each row
declare
  v_temp int;
begin
  select count(*) into v_temp from audit_emp_change where name=:old.ename;
  if v_temp=0 then
    insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
  else
    update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
  end if;
end;


4)、限制行触发器
说明:当使用行触发器时,默认情况下会在每个被作用行上执行一次触发器代码,
为了使在特定条件下执行行触发器代码,就需要使用when子句对触发条件加以限制。
例子:
Create or replace trigger tr_sal_sal
after update of sal on emp
for each row
when (old.job='SALESMAN')
declare
  v_temp int;
begin
  select count(*) into v_temp from audit_emp_change where name=:old.ename;
  if v_temp=0 then
    insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
  else
    update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
  end if;
end;


5)、DML触发器使用注意事项
编写DML触发器的时,触发器代码不能从触发器所对应的基表中读取数据。
例如,如果要要基于EMP表建立触发器,那么触发器代码不能包含对EMP表的查询操作。
此情况编译不会报错,执行会报错。




五、DML触发器的主要用途
说明:主要用于数据安全保护、数据审计、数据完整性、参照完整性、数据复制等功能。


1.控制数据安全
例如在非工作时间不能对EMP表做操作:
create or replace trigger tri_time
before insert or update or delete on emp
begin
  if to_char(sysdate,'hh24') not between '9' and '17' then
    raise_application_error(-20101,'非工作时间');
  end if;
end;
--本触发器如果在非工作时间内将不予更新emp表


2.实现数据审查与统计
Oracle本身提供了数据审计功能:audit insert,update,delete on emp by access;
设置后会将sql操作的信息写入到数据字典中,但这种审计只审计SQL操作,而不记录数据变化,
如果要审计数据变化,如下例:
create or replace trigger tri_etr
after delete on emp for each row
begin
  insert into emplog values(:old.ename,sysdate);
end;
--本触发器在emp表的记录被更新后,旧的记录插入到emplog表中。


3.实现数据完整性
数据完整性用约束实现性能是好的,例如alter table emp add constraint ck_sal check (sal>=800);
当约束无法实现特定的商业规则时可用触发器。
例如工资只升不降
create or replace trigger tri_salcheck
before update of sal on emp for each row
when (new.sal<old.sal or new sal>1.2*.old.sal)
begin
  raise_application_error(-20931,'工资只升不降,且升福不能超过20%');
end;


4.实现参照完整性
说明:约束可以实现级联删除,就如这样
alter table emp add constraint fk_deptno foreign key(deptno) references dept(deptno) on delete cascade;
却不能实现级联更新,此时可以用到触发器:
create or replace trigger update_cascade
after update of deptno on dept for each row
begin
  update emp set deptno=:new.deptno where deptno=:old.deptno;
end;
--参照完整性,利用触发器实现dept表的主键列和emp表的外部主键列的级联更新




六、INSTEAD OF触发器


1、概述
具有以下任一情况的视图不允许直接执行DML操作:
1)、具有集合操作符(union,union all,intersect,minus)
2)、具有分组函数(MIN,MAX,SUM,AVG,COUNT等)
3)、具有group by、connext by、start with等子句;
4)、具有distinct关键字
5)、具有连接查询。


为了在具有以上情况的复杂视图上执行DML操作,必须要基于视图建立INSTEAD OF触发器。
另外,instead of触发器只适用于视图,不能指定before和after选项


2、INSTEAD OF触发器的使用
建一个复杂视图
create or replace view dept_emp as
select a.deptno,a.dname,a.empno,b.ename
from dept a,emp b
where a.deptno=b.deptno;
建立一个INSTEAD OF触发器
create or replace trigger tr_instead_of_dept_emp
INSTEAD OF insert on dept_emp for each row
declare
  v_temp int;
begin
  select count(*) into v_temp from dept where deptno=:new.deptno;
  if v_temp=0 then
    insert into dept(deptno,dname) values(:new.deptno,:new.dname);
  end if;
  select count(*) into v_temp from emp where empno=:new.empno;
  if v_temp=0 then
    insert into emp(empno,ename,deptno) values(:new.empno,:new.ename,:new.deptno);
  end if;
end;




七、事件触发器
事件触发器是指基于Oracle事件(例如LOGON、STARTUP)所建立的触发器。通过使用事件触发器,提供了跟踪系统或数据库变化的机制。


常用的事件属性函数:
ora_client_ip_address         返回客户端的IP地址
ora_database_name             返回数据库名,类型是varchar2(50)
ora_dcs_cncrypted_password    返回DES加密后的用户口令
ora_dict_obj_name             返回DDL操作所对应的数据库对象名
ora_dict_obj_name_list        返回在事件中被修改的对象名列表
ora_dict_obj_owner            返回DDL操作所对应的对象的所有者名
ora_dict_obj_owner_list       返回在事件中被修改对象的所有者列表
ora_dict_obj_tpe              返回DDL操作所对应的数据对象类型
ora_grantee                   返回授权事件的授权者
ora_instance_num              返回列程号
ora_is_alter_column           检测特定列是否被修改
ora_is_creating_nested_table  检测是否正在建立嵌套表
ora_is_drop_column            检测特定列是否被删除
ora_is_servererror            检测是否返回了特定Oracle错误
org_login_user                返回登录用户名,类型varchar2(30)
ora_sysevent                  返回触发触发器的系统事件名


1、系统事件触发器
含义:是指由特定系统事件所触发的触发器,
包括4种事件:startup,shutdown,db_role_change,servererror
说明:系统事件触发器只能由SYS用户建立,并且shutdown abort命令不会触发shutdown事件。


例一(实例启动触发器)
create or replace trigger tri_startuplog
after startup on database    --startup触发器只能用after
begin
  insert into elog_table values(ora_sysevent,sysdate);
end;


例二(实例关闭触发器)
create or replace trigger tri_shutdownlog
before shutdown on database  --shutdown触发器只能用before
begin
  insert into elog_table values(ora_sysevent,sysdate);  --获取系统事件名和操作事件
end;


2、客户事件触发器
含义:是指基于客户事件建立的触发器,客户事件是指与用户登陆、注销、DDL及DCL相关的事件
说明:


例一(登陆触发器)
create or replace trigger tri_logon
after logon on database  --logon触发器只能用after
begin
  insert into logon_table values(ora_login_user,sysdate,ora_client_ip_address);  --获取用户名,操作事件,IP地址
end;


例二(退出触发器)
create or replace trigger tri_logoff
before logoff on database  --logoff触发器只能用before
begin
  insert into logon_table values(ora_login_user,sysdate,ora_client_ip_address);  --获取用户名,操作事件,IP地址
end;


3、DDL触发器
含义:记载DDL事件的触发器(CREATE,ALTER,DROP等)


例一(DDL触发器)
create or replace trigger tri_ddl
after ddl on scott.schema  --ddl触发器只能用after
begin
  insert into tb_table values(ora_sysevent,ora_login_user,ora_dict_obj_owner,ora_dict_obj_name,ora_dict_obj_type,sysdate);
end;
--记录用户scott所做的操作DDL操作




八、管理触发器


1、查询触发器信息
select * from user_triggers where table_name='EMP'


2、激活、禁止触发器
禁止某个触发器:alter trigger 触发器名 disable;
禁止某个表的所有触发器:alter table disable emp all triggers;
激活某个触发器:alter trigger 触发器名 enable;
激活某个表的所有触发器:alter table enable emp all triggers;


3、重新编译
当使用alter table命令修改表结构时(例如增加删除列),会使得其触发器转为invalid状态,这样为了触发器继续生效,需要重新编译触发器。
alter trigger 触发器名 compile


4、删除触发器
drop trigger 触发器名;


你可能感兴趣的:(oracle,触发器,trigger)