oracle触发器的实例(转)
- 格式:pdf
- 大小:180.58 KB
- 文档页数:6
oracle触发器的实例(转)
触发器使⽤教程和命名规范
⽬ 录
触发器使⽤教程和命名规范 11,触发器简介 1
2,触发器⽰例 2
3,触发器语法和功能 3
4,例⼀:⾏级触发器之⼀ 4
5,例⼆:⾏级触发器之⼆ 4
6,例三:INSTEAD OF触发器 6
7,例四:语句级触发器之⼀ 8
8,例五:语句级触发器之⼆ 9
9,例六:⽤包封装触发器代码 10
10,触发器命名规范 11
1,触发器简介
触发器(Trigger)是数据库对象的⼀种,编码⽅式类似存储过程,与某张表(Table)相关联,当有DML语句对表进⾏操作时,可以引起触
发器的执⾏,达到对插⼊记录⼀致性,正确性和规范性控制的⽬的。在当年C/S时代盛⾏的时候,由于客户端直接连接数据库,能保证数据
库⼀致性的只有数据库本⾝,此时主键(Primary Key),外键(Foreign Key),约束(Constraint)和触发器成为必要的控制机制。⽽触
发器的实现⽐较灵活,可编程性强,⾃然成为了最流⾏的控制机制。到了B/S时代,发展成4层架构,客户端不再能直接访问数据库,只有中
间件才可以访问数据库。要控制数据库的⼀致性,既可以在中间件⾥控制,也可以在数据库端控制。很多的青睐Java的开发者,随之将数据
库当成⼀个⿊盒,把⼤多数的数据控制⼯作放在了Servlet中执⾏。这样做,不需要了解太多的数据库知识,也减少了数据库编程的复杂性,
但同时增加了Servlet编程的⼯作量。从架构设计来看,中间件的功能是检查业务正确性和执⾏业务逻辑,如果把数据的⼀致性检查放到中间
件去做,需要在所有涉及到数据写⼊的地⽅进⾏数据⼀致性检查。由于数据库访问相对于中间件来说是远程调⽤,要编写统⼀的数据⼀致性
检查代码并⾮易事,⼀般采⽤在多个地⽅的增加类似的检查步骤。⼀旦⼀致性检查过程发⽣调整,势必导致多个地⽅的修改,不仅增加⼯作
量,⽽且⽆法保证每个检查步骤的正确性。触发器的应⽤,应该放在关键的,多⽅发起的,⾼频访问的数据表上,过多使⽤触发器,会增加
数据库负担,降低数据库性能。⽽放弃使⽤触发器,则会导致系统架构设计上的问题,影响系统的稳定性。
2,触发器⽰例
触发器代码类似存储过程,以PL/SQL脚本编写。下⾯是⼀个触发器的⽰例:
新建员⼯⼯资表salarycreate table SALARY
(
EMPLOYEE_ID NUMBER, --员⼯ID
MONTH VARCHAR2(6), --⼯资⽉份
AMOUNT NUMBER --⼯资⾦额
)
创建与salary关联的触发器salary_trg_rai1 Create or replace trigger salary_trg_rai
2 After insert on salary
3 For each row
4 declare
5 Begin
6 Dbms_output.put_line(‘员⼯ID:’ || :new.employee_id);
7 Dbms_output.put_line(‘⼯资⽉份:’ || :new.month);
8 Dbms_output.put_line(‘⼯资:’ || :new.amount);
9 Dbms_output.put_line(‘触发器已被执⾏’);
10 End;
打开⼀个SQL Window窗⼝(使⽤PL/SQL Developer⼯具),或在sqlplus中输⼊:Insert into salary(employee_id, month, amount) values(1, ‘200606’, 10000);
执⾏后可以在sqlplus中,或在SQL Window窗⼝的Output中见到
员⼯ID:1
⼯资⽉份:200606
⼯资:10000
触发器已执⾏
在代码的第⼀⾏,定义了数据库对象的类型是trigger,定义触发器的名称是salary_trg_rai
第⼆⾏说明了这是⼀个after触发器,在DML操作实施之后执⾏。紧接着的insert说明了这是⼀个针对insert操作的触发器,每个对该表进⾏的insert操作都会执⾏这个触发器。
第三⾏说明了这是⼀个针对⾏级的触发器,当插⼊的记录有n条时,在每⼀条插⼊操作时都会执⾏该触发器,总共执⾏n次。Declare后⾯跟的是本地变量定义部分,如果没有本地变量定义,此部分可以为空
Begin和end括起来的代码,是触发器的执⾏部分,⼀般会对插⼊记录进⾏⼀致性检查,在本例中打印了插⼊的记录和“触发器已执⾏”。
其中:new对象表⽰了插⼊的记录,可以通过:new.column_name来引⽤记录的每个字段值其中:new对象表⽰了插⼊的记录,可以通过:new.column_name来引⽤记录的每个字段值
3,触发器语法和功能
触发器的语法如下CREATE OR REPLACE TRIGGER trigger_name
ON table_name
[FOR EACH ROW]
WHEN (condition)
DECLARE
BEGIN
--触发器代码
END;
Trigger_name是触发器的名称。可以选择before或者after或instead of。Before表⽰在DML语句实施前执⾏触发
器,⽽after表⽰在在dml语句实施之后执⾏触发器,instead of触发器⽤在对视图的更新上。可以选择⼀个或多个DML语句,如果选择多个,则⽤or分开,如:insert or update。Table_name是触发器关联的表名。
[FOR EACH ROW]为可选项,如果注明了FOR EACH ROW,则说明了该触发器是⼀个⾏级的触发器,DML语句处理每条记录都会执⾏触
发器;否则是⼀个语句级的触发器,每个DML语句触发⼀次。WHEN后跟的condition是触发器的响应条件,只对⾏级触发器有效,当操作的记录满⾜condition时,触发器才被执⾏,否则不执⾏。
Condition中可以通过new对象和old对象(注意区别于前⾯的:new和:old,在代码中引⽤需要加上冒号)来引⽤操作的记录。
触发器代码可以包括三种类型:未涉及数据库事务代码,涉及关联表(上⽂语法中的table_name)数据库事务代码,涉及除关联表之外数据
库事务代码。其中第⼀种类型代码只对数据进⾏简单运算和判断,没有DML语句,这种类型代码可以在所有的触发器中执⾏。第⼆种类型代
码涉及到对关联表的数据操作,⽐如查询关联表的总记录数或者往关联表中插⼊⼀条记录,该类型代码只能在语句级触发器中使⽤,如果在
⾏级触发器中使⽤,将会报ORA-04091错误。第三种类型代码涉及到除关联表之外的数据库事务,这种代码可以在所有触发器中使⽤。
从触发器的功能上来看,可以分成3类: 重写列(仅限于before触发器)
采取⾏动(任何触发器)
拒绝事务(任何触发器)
“重写列”⽤于对表字段的校验,当插⼊值为空或者插⼊值不符合要求,则触发器⽤缺省值或另外的值代替,在多数情况下与字段的default属
性相同。这种功能只能在⾏级before触发器中执⾏。“采取⾏动”针对当前事务的特点,对相关表进⾏操作,⽐如根据当前表插⼊的记录更新
其他表,银⾏中的总帐和分户帐间的总分关系就可以通过这种触发器功能来维护。“拒绝事务”⽤在对数据的合法性检验上,当更新的数据不
满⾜表或系统的⼀致性要求,则通过抛出异常的⽅式拒绝事务,在其上层的代码可以捕获这个异常并进⾏相应操作。
下⾯将通过举例说明,在例⼦中将触发器主体的语法⼀⼀介绍,读者可以在例⼦中体会触发器的功能。
4,例⼀:⾏级触发器之⼀
CREATE OR REPLACE TRIGGER salary_raiu
AFTER INSERT OR UPDATE OF amount ON salary
FOR EACH ROW
BEGIN
IF inserting THEN
dbms_output.put_line(‘插⼊’);
ELSIF updating THEN
dbms_output.put_line(‘更新amount列’);
END IF;
END;
以上是⼀个after insert和after update的⾏级触发器。在第⼆⾏中of amount on salary的意思是只有当amount列被更新时,update触发器才会
有效。所以,以下语句将不会执⾏触发器:Update salary set month = ‘200601’ where month = ‘200606’;
在触发器主体的if语句表达式中,inserting, updating和deleting可以⽤来区分当前是在做哪⼀种DML操作,可以作为把多个类似触发器合并
在⼀个触发器中判别触发事件的属性。
5,例⼆:⾏级触发器之⼆
新建员⼯表employmentCREATE TABLE EMPLOYMENT
(
EMPLOYEE_ID NUMBER, --员⼯ID
MAXSALARY NUMBER --⼯资上限
)
插⼊两条记录Insert into employment values(1, 1000);
Insert into employment values(2, 2000);
CREATE OR REPLACE TRIGGER salary_raiu
AFTER INSERT OR UPDATE OF amount ON salary
FOR EACH ROW
WHEN ( NEW.amount >= 1000 AND (old.amount IS NULL OR OLD.amount <= 500))
DECLAREDECLARE
v_maxsalary NUMBER;
BEGIN
SELECT maxsalary
INTO v_maxsalary
FROM employment
WHERE employee_id = :NEW.employee_id;
IF :NEW.amount > v_maxsalary THEN
raise_application_error(-20000, '⼯资超限');
END IF;
END;
以上的例⼦引⼊了⼀个新的表employment,表中的maxsalary字段代表该员⼯每⽉所能分配的最⾼⼯资。下⾯的触发器根据插⼊或修改记录
的employee_id,在employment表中查到该员⼯的每⽉最⾼⼯资,如果插⼊或修改后的amount超过这个值,则报错误。
代码中的when⼦句表明了该触发器只针对修改或插⼊后的amount值超过1000,⽽修改前的amount值⼩于500的记录。New对象和old对象
分别表⽰了操作前和操作后的记录对象。对于insert操作,由于当前操作记录⽆历史对象,所以old对象中所有属性是null;对于delete操作,
由于当前操作记录没有更新对象,所以new对象中所有属性也是null。但在这两种情况下,并不影响old和new对象的引⽤和在触发器主体中
的使⽤,和普通的空值作同样的处理。
在触发器主体中,先通过:new.employee_id,得到该员⼯的⼯资上限,然后在if语句中判断更新后的员⼯⼯资是否超限,如果超限则错误代
码为-20000,错误信息为“⼯资超限”的⾃定义错误。其中的raise_application_error包含两个参数,前⼀个是⾃定义错误代码,后⼀个是⾃定