a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 96|回复: 1

[综合] Oracle辅导:ORACLE PL/SQL编程之把触发器说透

[复制链接]
发表于 2012-8-4 13:54:49 | 显示全部楼层 |阅读模式
 触发器是许多关系数据库系统都提供的一项技术。在ORACLE系统里,触发器类似过程和函数,都有声明,执行和异常处理过程的PL/SQL块。
) }" h* `+ L2 Z6 Y  1 触发器类型
$ p* E- n- g& {, k0 L7 x8 G  触发器在数据库里以独立的对象存储,它与存储过程和函数不同的是,存储过程与函数需要用户显示调用才执行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。所以运行触发器就叫触发或点火(firing)。ORACLE事件指的是对数据库的表进行的INSERT、UPDATE及DELETE操作或对视图进行类似的操作。ORACLE将触发器的功能扩展到了触发ORACLE,如数据库的启动与关闭等。所以触发器常用来完成由数据库的完整性约束难以完成的复杂业务规则的约束,或用来监视对数据库的各种操作,实现审计的功能。3 f! D5 x" V: V! G1 v3 y5 e
  1.1 DML触发器
" q+ j+ m( O0 n  ORACLE可以在DML语句进行触发,可以在DML操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
, \  x8 d# B: {: s  1.2 替代触发器! i3 C2 `& c, q. j
  由于在ORACLE里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。它就是ORACLE 8专门为进行视图操作的一种处理方法。2 r% u6 o- i, G
  系统触发器
0 e& o& W( c! x: m6 w  S  ORACLE 8i 提供了第三种类型的触发器叫系统触发器。它可以在ORACLE数据库系统的事件中进行触发,如ORACLE系统的启动与关闭等。3 k' r7 _! |9 [) y, x! w( Q, w
  1.3触发器组成:
4 e% T7 Y8 L! l1 ?2 [- q- H4 w6 ]  触发事件:引起触发器被触发的事件。 例如:DML语句(INSERT, UPDATE, DELETE语句对表或视图执行数据处理操作)、DDL语句(如CREATE、ALTER、DROP语句在数据库中创建、修改、删除模式对象)、数据库系统事件(如系统启动或退出、异常错误)、用户事件(如登录或退出数据库)。6 ]% b. s7 \5 _; }: u
  触发时间:即该TRIGGER 是在触发事件发生之前(BEFORE)还是之后(AFTER)触发,也就是触发事件和该TRIGGER 的操作顺序。$ q' u3 ]5 ?8 c
  触发操作:即该TRIGGER 被触发之后的目的和意图,正是触发器本身要做的事情。 例如:PL/SQL 块。
7 S3 m! _: u1 P4 H  触发对象:包括表、视图、模式、数据库。只有在这些对象上发生了符合触发条件的触发事件,才会执行触发操作。" r) P: O: \; ]* X* J6 s) [
  触发条件:由WHEN子句指定一个逻辑表达式。只有当该表达式的值为TRUE时,遇到触发事件才会自动执行触发器,使其执行触发操作。2 c3 Y6 |" A* n
  触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器。( S' Z8 M6 o+ _4 a. u, b3 L
  语句级(STATEMENT)触发器:是指当某触发事件发生时,该触发器只执行一次;
: R0 `; _2 [3 q0 ^' r3 [! P: {  行级(ROW)触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。
9 t0 m' e( J* s9 x  c7 _! N  编写触发器时,需要注意以下几点:
( ]* z2 ~; a5 R# J1 X( P0 _. O  触发器不接受参数。
. x$ A( W9 S# g6 ]9 p  一个表上最多可有12个触发器,但同一时间、同一事件、同一类型的触发器只能有一个。并各触发器之间不能有矛盾。
  l* O, u& s- [0 w# N  在一个表上的触发器越多,对在该表上的DML操作的性能影响就越大。6 k% E. @" k( U" l, [, f, W
  触发器最大为32KB。若确实需要,可以先建立过程,然后在触发器中用CALL语句进行调用。8 A6 U+ z' [8 U' V
  在触发器的执行部分只能用DML语句(SELECT、INSERT、UPDATE、DELETE),不能使用DDL语句(CREATE、ALTER、DROP)。- J: d7 \. H+ ~( N8 a& O4 {
  发器中不能包含事务控制语句(COMMIT,ROLLBACK,SAVEPOINT)。因为触发器是触发语句的一部分,触发语句被提交、回退时,触发器也被提交、回退了。5 w& I! ~+ Z6 O$ F9 U
  在触发器主体中调用的任何过程、函数,都不能使用事务控制语句。; A( E% ?- D. j- A; t9 S
  在触发器主体中不能申明任何Long和blob变量。新值new和旧值old也不能向表中的任何long和blob列。* k  I1 D% n7 I( V( K7 t
  不同类型的触发器(如DML触发器、INSTEAD OF触发器、系统触发器)的语法格式和作用有较大区别。
: n' ~" M: \: K( i) W  2 创建触发器
' o" c) E) n0 d& a7 l  创建触发器的一般语法是:+ K5 r2 S0 o- D3 O/ j2 X* [9 M
  CREATE [OR REPLACE] TRIGGER trigger_name* ?/ A( F/ {& [
  {BEFORE | AFTER }$ [# v5 i( h2 r7 H! e$ T+ [9 v
  {INSERT | DELETE | UPDATE [OF column [, column …]]}
# N0 N2 K7 u! f' u( S8 L% m3 v  [OR {INSERT | DELETE | UPDATE [OF column [, column …]]}...]: N# D! S4 d3 }+ l3 ]/ |
  ON [schema.]table_name | [schema.]view_name3 `& j. `8 D8 M; s+ i
  [REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]! s4 I" v6 `, y3 s2 F4 p, }
  [FOR EACH ROW ]1 T$ D# U* d% g1 W/ i# E
  [WHEN condition]7 k$ `. ^9 P- b: {" U
  PL/SQL_BLOCK | CALL procedure_name;  其中:8 ?) i, |/ |$ O1 C, H
  BEFORE 和AFTER指出触发器的触发时序分别为前触发和后触发方式,前触发是在执行触发事件之前触发当前所创建的触发器,后触发是在执行触发事件之后触发当前所创建的触发器。
: ^: m1 u+ K- U# X9 s3 S  ~  FOR EACH ROW选项说明触发器为行触发器。行触发器和语句触发器的区别表现在:行触发器要求当一个DML语句操走影响数据库中的多行数据时,对于其中的每个数据行,只要它们符合触发约束条件,均激活一次触发器;而语句触发器将整个语句操作作为触发事件,当它符合约束条件时,激活一次触发器。当省略FOR EACH ROW 选项时,BEFORE 和AFTER 触发器为语句触发器,而INSTEAD OF 触发器则只能为行触发器。
9 w/ k6 a; W) Q% }  REFERENCING 子句说明相关名称,在行触发器的PL/SQL块和WHEN 子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为OLD和NEW。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:),但在WHEN子句中则不能加冒号。
. ]  ]2 n2 {. v7 }; i7 K; {; ]  WHEN 子句说明触发约束条件。Condition 为一个逻辑表达时,其中必须包含相关名称,而不能包含查询语句,也不能调用PL/SQL 函数。WHEN 子句指定的触发约束条件只能用在BEFORE 和AFTER 行触发器中,不能用在INSTEAD OF 行触发器和其它类型的触发器中。/ y5 @/ T/ g" a% a: L
  当一个基表被修改( INSERT, UPDATE, DELETE)时要执行的存储过程,执行时根据其所依附的基表改动而自动触发,因此与应用程序无关,用数据库触发器可以保证数据的一致性和完整性。
% a; B$ _  A- `0 d( r6 i+ g  每张表最多可建立12 种类型的触发器,它们是:" }8 N; e. H( t7 V/ ^& S
  BEFORE INSERT& t5 [% {% t" k" p, Y4 n1 D3 v' X* K4 e
  BEFORE INSERT FOR EACH ROW3 ^6 C- ^$ K" M4 _
  AFTER INSERT9 f$ S( d& P4 C
  AFTER INSERT FOR EACH ROW
* ~# v$ X. W5 y, A6 N) z1 `  BEFORE UPDATE
& k5 ^8 [, k4 t! O& S1 |  BEFORE UPDATE FOR EACH ROW- P0 ~6 W. F, Z( C9 _
  AFTER UPDATE
. g9 p" Y1 ^2 W  T8 U6 B  AFTER UPDATE FOR EACH ROW& s% k! g* Z9 d
  BEFORE DELETE9 c4 P3 Z. A8 A1 t
  BEFORE DELETE FOR EACH ROW1 l2 o0 L. @: O+ c
  AFTER DELETE
3 g& n3 f  P% s7 k- z$ N% K  AFTER DELETE FOR EACH ROW
  p: L+ s( @1 F% G7 ?5 x  2.1 触发器触发次序* m9 u" B* i7 }% l* k# F- Q  S+ J4 [
  1、执行 BEFORE语句级触发器;) @' |! p2 s5 T6 A  y) G( n
  2、对与受语句影响的每一行:
" S6 ~- X6 h4 h  执行 BEFORE行级触发器
( ]2 K$ y7 x& Q3 W# }. j; R8 E1 c  执行 DML语句
* P& Y+ w# R4 ~% ?# w! Q3 }  执行 AFTER行级触发器
回复

使用道具 举报

 楼主| 发表于 2012-8-4 13:54:50 | 显示全部楼层

Oracle辅导:ORACLE PL/SQL编程之把触发器说透

  3、执行 AFTER语句级触发器& v& g: c7 P  A' s# i
  2.2 创建DML触发器
8 [- [" _  F) X2 w$ W/ z2 L2 P  触发器名与过程名和包的名字不一样,它是单独的名字空间,因而触发器名可以和表或过程有相同的名字,但在一个模式中触发器名不能相同。' I; {7 k$ g% G' S2 S- ~
  DML触发器的限制:# W0 l- w6 l% R$ S( c
  CREATE TRIGGER语句文本的字符长度不能超过32KB;
: u: A4 |! c! p9 J4 t! ]' v  触发器体内的SELECT 语句只能为SELECT … INTO …结构,或者为定义游标所使用的SELECT 语句。4 x) s1 R8 y6 x/ j: F9 c
  触发器中不能使用数据库事务控制语句 COMMIT; ROLLBACK, SVAEPOINT 语句;
. {, }/ a: l. U* U  ^) B/ W3 h  由触发器所调用的过程或函数也不能使用数据库事务控制语句;9 s" U- Q. c3 T/ K7 }
  触发器中不能使用LONG, LONG RAW 类型;
' p$ M3 a8 Z: c  发器内可以参照LOB 类型列的列值,但不能通过 :NEW 修改LOB列中的数据;
* e$ X7 D, R4 f/ M% m" ]  DML触发器基本要点:/ O$ V# Q4 J) M/ W3 j) D8 f5 {
  触发时机:指定触发器的触发时间。如果指定为BEFORE,则表示在执行DML操作之前触发,以便防止某些错误操作发生或实现某些业务规则;如果指定为AFTER,则表示在执行DML操作之后触发,以便记录该操作或做某些事后处理。6 n# [; y- n- B, M4 A( @7 }/ ^* \$ i
  触发事件:引起触发器被触发的事件,即DML操作(INSERT、UPDATE、DELETE)。既可以是单个触发事件,也可以是多个触发事件的组合(只能使用OR逻辑组合,不能使用AND逻辑组合)。
8 x( H; c( k, j  m4 \' `  条件谓词:当在触发器中包含多个触发事件(INSERT、UPDATE、DELETE)的组合时,为了分别针对不同的事件进行不同的处理,需要使用ORACLE提供的如下条件谓词。
3 K) i" }& _/ z$ R: d  1)、INSERTING:当触发事件是INSERT时,取值为TRUE,否则为FALSE。
& ~: t! ]9 z) c; q7 J$ t, M3 Z9 z  2)、UPDATING [(column_1,column_2,…,column_x)]:当触发事件是UPDATE      时,如果修改了column_x列,则取值为TRUE,否则为FALSE。其中column_x是可选的。
9 O- }. q2 |6 B7 k8 y+ `  3)、DELETING:当触发事件是DELETE时,则取值为TRUE,否则为FALSE。- H% f1 ]" P1 x* w" I, o) G. C
  解发对象:指定触发器是创建在哪个表、视图上。
2 ?' g% ~* t/ \0 g3 a! \  触发类型:是语句级还是行级触发器。
0 m/ H" G0 f0 P; L/ W  触发条件:由WHEN子句指定一个逻辑表达式,只允许在行级触发器上指定触发条件,指定UPDATING后面的列的列表。0 G0 ]- y8 T' L$ X
  问题:当触发器被触发时,要使用被插入、更新或删除的记录中的列值,有时要使用操作前、后列的值.4 ^2 G: |4 ^& X/ ]
  实现:  :NEW 修饰符访问操作完成后列的值$ V  ]8 X& L% c7 ^) j
  :OLD 修饰符访问操作完成前列的值/ R! j& M! N* J1 `7 E
  特性2 \! ~6 p, U1 D0 \. Z* M
  INSERT3 x9 l: H$ s; F( x- X0 Q8 B
  UPDATE
/ v! ?+ X  [. c# \! q5 a/ k  DELETE
* s0 m8 f% F3 u1 N" R: a5 h  OLD
/ i$ J& ~# z* ~! t  NULL/ R& v, j$ ^7 t* C- n, ^
  实际值
4 {# M* q0 |3 ~: S* h7 ?  实际值6 e, |1 Y/ ^$ B! _+ |, p4 x
  NEW
  S2 ~: Z! D5 _  实际值: `6 M, u0 }: d( o9 v; Z
  实际值
* y4 k- Q! |$ B+ O0 G' ?8 N' o; s  NULL4 G/ U2 e6 p: l& w& N+ ^
  例1: 建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去。
1 }. \" l8 V! H0 T  CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2;$ `- B8 l3 ?4 u3 O3 ?( \% Y
  CREATE OR REPLACE TRIGGER tr_del_emp$ L# s2 l. _+ M6 w4 X& z1 W
  BEFORE DELETE --指定触发时机为删除操作前触发( k) m) ]5 |$ n& r+ ]
  ON scott.emp
$ e/ e' f% H, c$ D& \5 i  FOR EACH ROW   --说明创建的是行级触发器
- Q; K; W9 T6 m( ^# M, Q  BEGIN" E$ T/ r! q, B
  --将修改前数据插入到日志记录表 del_emp ,以供监督使用。9 O- h# X6 Q$ p0 P
  INSERT INTO emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate )
! F9 R* s6 z- Q0 V0 n9 n* F4 w  VALUES( :old.deptno, :old.empno, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate );
4 a: N# k) S0 c' U+ N" o  END;$ Q/ A/ s- [- W+ k+ t9 X5 V0 M
  DELETE emp WHERE empno=7788;9 \0 Y# L0 T% Z. L; M) u, R, G
  DROP TABLE emp_his;6 R1 r( _# y* u! i" A
  DROP TRIGGER del_emp;  例2:限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围,即不允许在非工作时间修改departments表。$ A; y' C$ b) R% J" S
  CREATE OR REPLACE TRIGGER tr_dept_time
- s: G5 P& [7 L( f) g  }: V& \$ w  BEFORE INSERT OR DELETE OR UPDATE  `3 Q! A; _  Q( r
  ON departments) [- t1 d+ Y& x( Z" u- o
  BEGIN
7 p. \0 g# F: Z3 q  IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日')) OR (TO_CHAR(sysdate, 'HH24:MI') NOT BETWEEN '08:30' AND '18:00') THEN
) Z9 y3 K; X! I: H* j. N& P9 V4 p0 ~  RAISE_APPLICATION_ERROR(-20001, '不是上班时间,不能修改departments表');) f% _1 z$ f( n: Y2 w
  END IF;  o, r$ f! F  A# X. X+ L1 ?
  END;  例3:限定只对部门号为80的记录进行行触发器操作。
# w$ l9 n& ~" r6 u& M' G# j! h- S  CREATE OR REPLACE TRIGGER tr_emp_sal_comm
, _! |$ j, T( g4 D  _9 A$ j  BEFORE UPDATE OF salary, commission_pct
9 ?  g5 C) z/ n+ Q  OR DELETE! W0 N( J9 w( @' N/ s
  ON HR.employees
' v% i: g, w3 Z3 H* ~  FOR EACH ROW
) p' R6 Q8 K6 i/ L; F  WHEN (old.department_id = 80)# Y, t5 B4 Y! \1 f9 p0 N
  BEGIN$ N0 A) `. q4 u5 `7 [: ^: |& i( s
  CASE3 |2 ~- h' V4 B, r. e  t  N
  WHEN UPDATING ('salary') THEN% \2 w, L" B/ w! ]7 O/ o$ e& p. O
  IF :NEW.salary < :old.salary THEN
3 k5 m* P2 L* i* c" U  RAISE_APPLICATION_ERROR(-20001, '部门80的人员的工资不能降');& Z: s- C3 W+ K2 P- M
  END IF;
, c" }$ |( ?5 T  V  WHEN UPDATING ('commission_pct') THEN6 ^  k/ f. l8 Y) U$ ?4 D0 i
  IF :NEW.commission_pct < :old.commission_pct THEN0 M* w: q7 Y- s
  RAISE_APPLICATION_ERROR(-20002, '部门80的人员的奖金不能降');
$ @" {4 a4 \. x( v: B! \- n# }  END IF;
# T: E, I1 g7 k& ]! y$ j/ A- A  WHEN DELETING THEN: N) H( N3 z& o+ ]/ Q. _  _
  RAISE_APPLICATION_ERROR(-20003, '不能删除部门80的人员记录');: d8 [" ?/ E- ^1 Z& i* Q
  END CASE;
+ N" M8 e% Z" e7 C, q  END;
, w  B' b6 D3 q$ @) b, X+ D) E9 @  /*
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-21 07:57 , Processed in 0.308272 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表