我是靠谱客的博主 无聊电话,这篇文章主要介绍Oracel触发器,现在分享给大家,希望可以做个参考。

 

一、什么是触发器

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

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

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

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

对数据库表的DML操作

对视图的操作

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

 

二、触发器的作用

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

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

 

三、触发器的类型

•         DML 触发器

•         替代触发器

•          系统触发器

四、创建触发器的语法

复制代码
1
2
3
4
5
6
7
8
9
CREATE [OR REPLACE] TRIGGER trigger_name {BEFORE | AFTER | INSTEAD OF} [FOR EACH ROW] 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触发

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

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

 

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

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


 

Dml 触发器案例三

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

分析思路:

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

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

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

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
create table kucun(pid number,num number); create table churuku(pid number,inorout varchar2( 5 ),n number); insert into kucun values( 123 , 20 ); insert into kucun values( 321 , 20 ); create or replace trigger mytri3 before insert on churuku for each row declare i number; begin if :new.inorout= 'out' then select num into i from kucun where pid=:new.pid; if i >= :new.n then update kucun set num=num-:new.n where pid=:new.pid; else raise_application_error(- 20009 , ' 库存不足' ); end if; else update kucun set num=num+:new.n where pid=:new.pid; end if; end; insert into churuku values( 123 , 'in' , 10 ); select * from kucun 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表制做一个标识列

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

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

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

语法:

复制代码
1
2
3
Referencing [old as :old_name] [new as :new_name]


 

例:修改上例的触发器

复制代码
1
2
3
4
5
6
7
8
9
10
create or replace trigger genstuid before insert or update on students referencing new as new_stu for each row begin select stu_num.nextval into :new_stu.id from dual; end;


 

触发器谓语:inserting、updating、deleting

表达式

谓语状态

Inserting

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

Updating

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

deleting

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

 

例:

-- 学员注册信息表

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

i 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 、删除触发器

语法:

    

复制代码
1
2
Drop trigger 触发器名;


 

2 、禁止与允许触发器

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

语法:

    

复制代码
1
2
Alter trigger 触发器名 disable|enable;


 

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

语法:

    

复制代码
1
2
Alter table 表名 disable|enable all triggers;



 

最后

以上就是无聊电话最近收集整理的关于Oracel触发器的全部内容,更多相关Oracel触发器内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(75)

评论列表共有 0 条评论

立即
投稿
返回
顶部