1)触发器概述:
触发器包含三部分:
1,触发事件:可以是DML,系统关闭,用户登录等;
2,触发条件:是由when指定的;
3,触发操作:plsql块指定,且这个块最大只有32K,但是可以用call调用 其它子程序,所以打破了限制;
还有就是块中只能包含:select和DML,而不能包含DDL和TCL等。
对于触发器plsql中的块,不可以有显式的TCL或者隐式的TCL(也就是说trigger没权控制事务,其码对于DML触发器是这样的):
显式TCL:
scott@orcl>create or replace trigger test_trigger
2 after delete on emp
3 begin
4 commit;
5 end;
6 /
触发器已创建
scott@orcl>delete from emp;
delete from emp
*
第 1 行出现错误:
ORA-04092: COMMIT 不能在触发器中
隐式TCL:
scott@orcl>create or replace trigger test_trigger
2 after delete on emp
3 begin
4 execute immediate 'create table test(x int)';
5 end;
6 /
触发器已创建
scott@orcl>delete from emp;
delete from emp
*
第 1 行出现错误:
ORA-04092: COMMIT 不能在触发器中
2)触发器的类型:
触发时机分为:语句级和行级
语句级只是在DML执行前或者后完成相应的动作;
行级触发器
行级的针对DML影响的每一行都会有相应的反应。
按照触发时机:before和after
也就是在DML前执行还是后执行。
语句级before示例:
create or replace trigger emp_ins_trigger
before insert on emp
begin
if(to_char(sysdate,'q') in (2,3)) then raise_application_error(-20001,'第二、三季度不可以雇佣员工!');
end if;
end;
语句级after示例(经常用来DML后作审计之用):
create or replace trigger test_dml_trigger
after update or delete or insert on emp
declare v_sal emp.sal%type;
begin
select sum(sal) into v_sal from emp;
dbms_output.put_line('Now the total salary is: '||v_sal);
end;
when的使用:
只对行级触发器生效,因为语句级的只执行一次,不存在什么其它情况。
trigger的另一个注意点:
行级触发器,不可以读表数据,而语句级的可以:
create or replace trigger test_trigger
before update of sal on emp
declare maxsal number;
begin
select max(sal) into maxsal from emp;
dbms_output.put_line(maxsal);
end;
scott@orcl>update emp set sal=2000 where ename='SCOTT';
5000
create or replace trigger test_trigger
before update of sal on emp
for each row when(old.deptno=10)
declare maxsal number;
begin
select max(sal) into maxsal from emp;
dbms_output.put_line(maxsal);
end;
所以然在编译时可以通过,但是实际上在触发时会显示错误:
ORA-04091: 表 SCOTT.EMP 发生了变化, 触发器/函数不能读它
3)DML触发器应用:
1,数据完整性:
create or replace trigger emp_upd_tri
before update on emp for each row
when(new.sal<old.sal)
begin
raise_application_error(-20001,'员工工资只能涨,不可以降!');
end;
2,参照完整性:
create or replace trigger dept_upd_tri
before update of deptno on dept
for each row
begin
update emp set deptno=:new.deptno where deptno=:old.deptno;
end;
4)instead of触发器:
更新连接视图时,以触发器的plsql块来替代执行针对视图上的DML,因为复杂视图是不可以直接DML的,当然如果有必要的键值保存就可以更新。
scott@orcl>create table dept_emp as select d.deptno deptno,dname,loc,ename,empno,job,mgr,sal,e.deptno edeptno from emp e,dept d where e.deptno=d.deptno;
scott@orcl>insert into dept_emp values(22,'TEST','ANHUI','ZHY',2255,'TOMSOO',7788,1000,22);
scott@orcl>update dept_emp set loc='KDF' where deptno=22;
scott@orcl>delete from dept_emp where deptno=22;
所谓的键值保存,其实就是在针对视图DML时不违反完整性约束就行了。
而instead of触发器是为了更好的更新视图,从而维护完整性:
create or replace trigger dept_emp_instd_tri
instead of insert on dept_emp for each row
begin
.
.
end;
这里要注意的是语法,instead of dml on tabname for each row,这也是固定语法。
5)系统触发器的使用:
要记住的相关系统事件属性函数,且这些只有在触发器中可以使用:
ora_client_ip_address
ora_des_encrypted_password
ora_dict_obj_name
ora_dict_obj_name_list(namelist out ora_name_list_t)
ora_dict_obj_owner
ora_dict_obj_owner_list(owner_list out ora_name_list_t)
ora_dict_obj_type
ora_grantee(user_list out ora_name_list_t) 返回授权人
ora_database_name
ora_instance_name
ora_login_user 看登录用户名
ora_is_alter_column(column_name) 看列是否被修改
ora_is_drop_column(column_name) 看列是否被删除
ora_is_creating_nested_table 检测是否在创建嵌套表
ora_is servererror(errornumber) 是否返回了特定的oracle号
ora_sysevent 系统事件名
ora_sql_txt
建立时常用关键词:
after startup on database
before shutdown on database
after logon on database
before logoff on database
after ddl on user.schema
create or replace trigger test_trigger
after ddl on scott.schema
begin
if(ora_is_alter_column('ENAME')) then
insert into test values('ename has modify',ora_dict_obj_name);
end if;
end;
scott@orcl>alter table emp modify ename varchar2(30);
上面这个ddl就会触发相应的触发器。
logon 触发器一例:
create or replace trigger logon_db_tri
after logon on database
begin
if(dbms_session.is_role_enabled('connect'))
then
insert into test values('connect',ora_des_encrypto_password);
end if;
end;
ora_sql_txt监视完整ddl语句:
create or replace trigger monitor_ddl_tri
after ddl on scott.schema
declare
sql_txt ora_name_list_t;
n pls_integer;
sql_code pls_integer;
sql_errm varchar2(1000);
sql_stmt varchar2(1000);
begin
if(ora_is_drop_column('COMM'))
then
n:=ora_sql_txt(sql_txt);
for i in 1..n
loop
sql_stmt:=sql_stmt||sql_txt(i);
end loop;
insert into test values(ora_dict_obj_owner,sql_stmt);
end if;
exception
when others then
sql_errm:=sqlerrm;
sql_code:=sqlcode;
dbms_output.put_line(sql_code||'错误消息是: '||sql_errm);
end;