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;