Oracel触发器

一、什么是触发器

    触发器类似于函数和过程,它们都是具有声明、执行部分和异常处理部分的PL/SQL块。

    触发器必须在数据库中以独立对象的身份存储,不能定义到包中。

    过程是显式地通过调用而执行的,并且可以传参;与之相反,触发器是在事件发生时隐式地运行的,并且触发器不能接收参数。

    运行触发器的方式叫做激发(firing)触发器。其触发的事件可以是:

对数据库表的DML操作

对视图的操作

可以激发系统事件(如:启动、关闭数据库)……

 

二、触发器的作用

1 、可以用来自动审计数据内容。

2 、在内容发生变更时,自动通知其他程序采取相应的处理。

 

三、触发器的类型

•         DML 触发器

•         替代触发器

•          系统触发器

四、创建触发器的语法

[sql]  view plain  copy
  1. CREATE [OR REPLACETRIGGER trigger_name   
  2.   
  3. {BEFORE | AFTER | INSTEAD OF}  
  4.   
  5.  [FOR EACH ROW]  
  6.   
  7. trigger_body;   

 

其中,trigger_name是触发器的名称,triggering_event说明了激发触发器的事件(也可能包括特殊的表或视图),trigger_body是触发器的代码。referencing_clause用来引用正在处于修改状态下的行中的数据,如果在WHEN子句中指定trigger_condition的话,则首先对该条件求值。触发器主体只有在该条件为真值时才运行。

注意触发器主体不能超过32K。

 

五、DML触发器

(一)分类

1 、根据触发事件划分

insert/update/delete

 

2 、根据触发时间划分

before—— 先触发后执行

after—— 先执行后触发

 

3 、根据触发级别划分

语句级:每个dml语句不论影响了多少行都只触发1次

记录级:针对dml语句影响的每个行都会触发一次

 

Dml 触发器案例一

要求:用户下班时间和周末不能更改emp表

分析思路:

触发事件:对emp表的update、insert、delete操作

触发时间:使用前触发可以避免不必要的回滚所以选择before触发

触发级别:每个语句触发一次就可以

代码描述:当用户对表进行更改操作的时候,判断当前的系统时间,如果是周末或者下班时间就回滚或抛出异常

 

[sql]  view plain  copy
  1. create or replace trigger myti2  
  2.   before insert or update or delete on emp  
  3. declare   
  4.   i varchar2( 20 );  
  5.   j varchar2( 20 );  
  6. begin   
  7.   select to_char(sysdate, 'day' ) into i from dual;  
  8.   select to_char(sysdate, 'hh24' ) into j from dual;  
  9.   if i= ' 星期六' then   
  10.      raise_application_error(- 20082 , ' 不能在非工作时间更改表' ); -- 注意,错误号在20000--20999  
  11.   elsif i= ' 星期日' then   
  12.      raise_application_error(- 20082 , ' 不能在非工作时间更改表' );  
  13.   elsif j< '08' then   
  14.      raise_application_error(- 20082 , ' 不能在非工作时间更改表' );  
  15.   elsif j> '17' then   
  16.      raise_application_error(- 20082 , ' 不能在非工作时间更改表' );  
  17.   else   
  18.      null;  
  19.   end if;    
  20. end myti2;  
  21.   
  22. insert into emp(empno) values( 9999 );   


 

说明:Oracle的触发器中不支持回滚操作。

 

Dml 触发器案例二

要求:用户对emp表的sal更改量不能超过10%

分析思路:

触发事件:对emp表的update操作

触发时间:使用前触发可以避免不必要的回滚所以选择before触发

触发级别:因为会影响到多行因此使用行触发,针对被更改的每一行都触发一次

代码描述:当用户对表进行更改操作的时候,根据更改后的sal(使用:new.sal)和更改前的sal(使用:old.sal),判断更改量是否超过10%,如果超出了就抛出异常。

注意:如果要使用:old和:new一定是行触发(for each row)

[sql]  view plain  copy
  1. create or replace trigger myti1  
  2.   before update on emp for each row   
  3. begin   
  4.   if abs(:new.sal-:old.sal)/:old.sal > 0.1 then   
  5.     raise_application_error(- 20001 , ' 改动过大' );  
  6.   end if;  
  7. end;   
  8.     
  9.   
  10. update emp set sal= 1500 where empno= 7369 ;   


 

Dml 触发器案例三

要求:现在有两个表,商品库存表,商品交易表,如果交易表中插入记录a商品进货10个,那么a商品的库存就增加10个,如果销售10个,a的库存就减少10个,如果请求的数量超出库存,我们就提示出错

分析思路:

触发事件:对商品交易表的insert操作

触发时间:使用前触发可以避免不必要的回滚所以选择before触发

触发级别:因为要使用:old和:new一定是行触发(for each row)

代码描述:根据插入的记录首先判断交易类型,如果是进货就更改商品库存表把相应商品的库存加上进货数量,如果是出货首先判断该商品是否有足够的库存,如果有就更改该库存,如果库存不足就抛出异常。

[sql]  view plain  copy
  1. create table kucun(pid number,num number);  
  2.   
  3. create table churuku(pid number,inorout varchar2( 5 ),n number);  
  4.   
  5. insert into kucun values( 123 , 20 );  
  6. insert into kucun values( 321 , 20 );  
  7.   
  8. create or replace trigger mytri3  
  9.   before insert on churuku for each row   
  10. declare   
  11.   i number;  
  12. begin   
  13.   if :new.inorout= 'out' then   
  14.     select num into i from kucun where pid=:new.pid;  
  15.     if i >= :new.n then   
  16.       update kucun set num=num-:new.n where pid=:new.pid;  
  17.     else   
  18.       raise_application_error(- 20009 , ' 库存不足' );  
  19.     end if;  
  20.   else   
  21.     update kucun set num=num+:new.n where pid=:new.pid;  
  22.   end if;  
  23. end;  
  24.   
  25. insert into churuku values( 123 , 'in' , 10 );  
  26.   
  27. select * from kucun  
  28.   
  29. select * from churuku   


 

六、详解DML触发器

DML 触发器是由对数据库表进行insert、update、delete操作而激发的触发器

DML 触发器可以设定语句、定时和触发级别

DML 触发器类型

类别

说明

语句

insert 、update、delete

定义何种DML语句激发触发器

定时

before 、after

定义触发器是在语句运行前或运行后激发

级别

语句级、行级

如果是行级触发器,该触发器就对由触发语句变更的每一行激发一次。

如果是语句级触发器,则该触发器就在语句之前或之后激发一次。

行级触发器定义语句for each row

 

DML 触发器激发顺序:

1、  语句级、之前级触发器

2、  行级触发器

(1)之前级触发器

(2)执行该语句本身(insert、update、delete)

(3)之后级触发器

3 、语句级、之后级触发器

 

行级触发器的相关标识

行级触发器是按触发语句所处理的行激发的。在触发器内,我们可以访问正在处理中的行的数据。行级访问的两个PL/SQL宿主变量:old、:new

触发语句

:old

:new

Insert

无定义,所有列为null

将插入的值

Update

更新前,行的原始值

将更新的值

delete

行删除前的原始值

无定义,所有列为null

——

具有只读属性,只能读入

可以修改值

例:为students表制做一个标识列

[sql]  view plain  copy
  1. create table students(id number( 5 ),first_name varchar2( 10 ),last_name varchar2( 10 ));  
  2.   
  3. create sequence stu_num  
  4.   
  5. create or replace trigger genstuid  
  6. before insert on students for each row   
  7. begin   
  8.    select stu_num.nextval into :new.id from dual;  
  9. end;  
  10.   
  11. insert into students(first_name,last_name) values'Anni' , 'Scott' );  
  12.   
  13. select * from students   

说明::old和:new记录不能传递到过程和函数中。

为:old和:new指定一个不同的名称

语法:

[sql]  view plain  copy
  1. Referencing [old as :old_name] [new as :new_name]   


 

例:修改上例的触发器

[sql]  view plain  copy
  1. create or replace trigger genstuid  
  2. before insert or update on students  
  3. referencing new as new_stu  
  4. for each row   
  5. begin   
  6.    select stu_num.nextval into :new_stu.id from dual;  
  7. end;   
  8.     


 

触发器谓语:inserting、updating、deleting

表达式

谓语状态

Inserting

如果触发语句是insert的话,则为真(true),否则为假(false)

Updating

如果触发语句是update的话,则为真(true),否则为假(false)

deleting

如果触发语句是delete的话,则为真(true),否则为假(false)

 

例:

-- 学员注册信息表 

[html]  view plain  copy
  1. create table rs_stu(stu_id number( 5 ),dep char( 10 ),course number( 10 ),grade char( 10 ))  
  2.   
  3. -- 学员信息审计表(日志)   
  4. create table stu_audit(change_type char( 1 ) not null,   
  5.                        changed_by varchar2( 8 ) not null,  
  6.                        timestamp date not null,  
  7.                        old_stu_id number( 5 ),  
  8.                        old_dep char( 10 ),  
  9.                        old_course number( 10 ),  
  10.                        old_grade char( 10 ),  
  11.                        new_stu_id number( 5 ),  
  12.                        new_dep char( 10 ),  
  13.                        new_course number( 10 ),  
  14.                        new_grade char( 10 ));  
  15.   
  16. -- 针对增、删、改的审计触发器                          
  17. create or replace trigger logstuchanges  
  18. before insert or delete or update on rs_stu for each row   
  19. declare   
  20. v_changetype char( 1 );  
  21. begin   
  22.      if inserting then   
  23.         v_changetype:'I' ;  
  24.      elsif updating then   
  25.         v_changetype:'U' ;  
  26.      else   
  27.         v_changetype:'D' ;  
  28.      end if;  
  29.      insert into stu_audit(change_type,changed_by,timestamp,  
  30. old_stu_id,old_dep,old_course,old_grade,  
  31.                        new_stu_id,new_dep,new_course,new_grade)  
  32.       values(v_changetype,user,sysdate,  
  33.             :old.stu_id,:old.dep,:old.course,:old.grade,  
  34.             :new.stu_id,:new.dep,:new.course,:new.grade);  
  35. end;  
  36.   
  37. -- 测试   
  38. insert into rs_stu values( 200 , 'B' , 3 , 2 );  
  39.   
  40. update rs_stu set dep'C' where stu_id200   
  41.   
  42. delete from rs_stu  
  43.   
  44. select * from rs_stu  
  45.   
  46. select * from stu_audit   


 

七、替代触发器

1 、定义

    DML 触发器是除去执行insert、update或delete操作外,还要被激活运行的触发器,而替代触发器则被激发来代替执行DML语句。

    替代触发器还可以定义在视图上,而DML触发器只能定义在表上。

    替代触发器是行级的。

 

2 、用途

(1)允许对无法变更的视图进行修改;

(2)修改视图中嵌套表的列。

 

3 、可变更的与不可变更的视图

一般来说,视图如果不包括下列命令的就是一个可变更的视图:

l         集合操作(union,union all,minus,intersect)

l         聚合函数(sum,avg,count)

l         Group by

l         Distinct

l         联合查询

 

例:

-- 分组计算每个部门的总销售额的视图

create view myview as 
select
 deptno,sum(sal) tot from emp group by deptno;

-- 让10部门销售额翻一番 
update myview set tot=tot* 2 where deptno= 10 ;

 

说明:这个对视图的更新不会成功,因为该视图用了聚合函数、分组 

-- 我们做个替代触发器,来代替上面的更新语句完成任务 
create or replace trigger mytri instead of update on myviewfor each row 
declare
 
number;
begin 

select count(*) into i from empwhere deptno=:new.deptno;
   update emp set sal=sal+(:new.tot-:old.tot)/iwhere deptno=:new.deptno;
end;

 

4 、一个复杂的不可更该视图的替代触发器

例:

-- 分组计算每个部门的工资总额的视图

create view v1(deptno,sal) as 
  select
 deptno,sum(sal) from emp group by deptno;

-- 计算工资的触发器。例如,我们将10部门的工资从6000改为20000,我们就把增长的部分按比例分配给该部门的每个员工。反之如此 
create or replace trigger
 tu instead of update on v1 for each row 
declare
 
  w number;
  s emp.sal%type;
  cursor cur is select * from empwhere deptno=:new.deptno;
  q number;
  r number;
  temp number;
begin 
  s:=:new.sal-:old.sal;  -- 修改的差值 
  temp:=s;  -- 修改后的值 
  select count(*) into q from empwhere deptno=:new.deptno;
  if s>= 0 then 
    for
 i in cur
    loop 

if cur%rowcount<q then 
        w:=i.sal/:old.sal;   -- 先求每个职工的工资百分率 
        temp:=temp-abs(w*s); --w*s 是按比例分配更新值 
        update emp set sal=sal+w*s where empno=i.empno;
       elsif cur%rowcount = q then 
        update
 emp set sal=sal+temp where empno=i.empno;
      end if;
    end loop;
  else 
    for
 i in cur
    loop 
      if
 cur%rowcount < q then 
        w:=i.sal/:old.sal;
        temp:=temp+abs(w*s);
        update emp set sal=sal+w*s where empno=i.empno;
      elsif cur%rowcount = q then 
        update
 emp set sal=sal+temp where empno=i.empno;
      end if;
    end loop;
  end if;
end;

 

-- 修改上例,四舍五入到整数

create or replace trigger tu instead of update on v1for each row 
declare
 
  w number;
  s emp.sal%type;
  cursor cur is select * from empwhere deptno=:new.deptno;
  q number;
  r number;
  temp number;
begin 
  s:=:new.sal-:old.sal;  -- 修改的差值 
  temp:=s;  -- 修改后的值 
  select count(*) into q from empwhere deptno=:new.deptno;
  if s>= 0 then 
    for
 i in cur
    loop 
      if
 cur%rowcount<q then 
        w:=i.sal/:old.sal;   -- 先求每个职工的工资百分率 
        select round(w*s,0) into r from dual;
        temp:=temp-abs(r);
        update emp set sal=sal+r where empno=i.empno;
      elsif cur%rowcount = q then 
        update
 emp set sal=sal+temp where empno=i.empno;
      end if;
    end loop;
  else 
    for
 i in cur
    loop 

if cur%rowcount < q then 
        w:=i.sal/:old.sal;
        select round(w*s,0) into r from dual;
        temp:=temp+abs(r);
        update emp set sal=sal+r where empno=i.empno;
      elsif cur%rowcount = q then 
        update
 emp set sal=sal+temp where empno=i.empno;
      end if;
    end loop;
  end if;
end;

 

-- 删除语句触发器 
create or replace trigger
 tid instead of insert or delete on v1
begin 
  dbms_output.put_line( ' 本视图只能进行更新,没有插入和删除功能!' );
end;

 

-- 测试 
select 
from emp where deptno= 10 ;
update v1 set sal= 17000 where deptno= 10 ;
select * from emp where deptno= 10 ;
rollback;
delete from v1 where deptno= 10 ;

 

八、删除、禁止触发器

 

1 、删除触发器

语法:

    

[sql]  view plain  copy
  1. Drop trigger 触发器名;   


 

2 、禁止与允许触发器

    与过程和包不同,触发器可以被禁止使用

语法:

    

[sql]  view plain  copy
  1. Alter trigger 触发器名 disable|enable;   


 

3 、禁止与允许整个表的触发器

语法:

    

[sql]  view plain  copy
  1. Alter table 表名 disable|enable all triggers;   



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值