Oracle触发器使用教程和命名规范V10Word文件下载.docx
- 文档编号:20783478
- 上传时间:2023-01-25
- 格式:DOCX
- 页数:12
- 大小:24.17KB
Oracle触发器使用教程和命名规范V10Word文件下载.docx
《Oracle触发器使用教程和命名规范V10Word文件下载.docx》由会员分享,可在线阅读,更多相关《Oracle触发器使用教程和命名规范V10Word文件下载.docx(12页珍藏版)》请在冰豆网上搜索。
要控制数据库的一致性,既可以在中间件里控制,也可以在数据库端控制。
很多的青睐Java的开发者,随之将数据库当成一个黑盒,把大多数的数据控制工作放在了Servlet中执行。
这样做,不需要了解太多的数据库知识,也减少了数据库编程的复杂性,但同时增加了Servlet编程的工作量。
从架构设计来看,中间件的功能是检查业务正确性和执行业务逻辑,如果把数据的一致性检查放到中间件去做,需要在所有涉及到数据写入的地方进行数据一致性检查。
由于数据库访问相对于中间件来说是远程调用,要编写统一的数据一致性检查代码并非易事,一般采用在多个地方的增加类似的检查步骤。
一旦一致性检查过程发生调整,势必导致多个地方的修改,不仅增加工作量,而且无法保证每个检查步骤的正确性。
触发器的应用,应该放在关键的,多方发起的,高频访问的数据表上,过多使用触发器,会增加数据库负担,降低数据库性能。
而放弃使用触发器,则会导致系统架构设计上的问题,影响系统的稳定性。
触发器代码类似存储过程,以PL/SQL脚本编写。
下面是一个触发器的示例:
新建员工工资表salary
create
table
SALARY
(
EMPLOYEE_ID
NUMBER,--员工ID
MONTH
VARCHAR2(6),--工资月份
AMOUNT
NUMBER
--工资金额
)
创建与salary关联的触发器salary_trg_rai
1
Createorreplacetriggersalary_trg_rai
2
Afterinsertonsalary
3
Foreachrow
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;
打开一个SQLWindow窗口(使用PL/SQLDeveloper工具),或在sqlplus中输入:
Insertintosalary(employee_id,month,amount)values(1,‘200606’,10000);
执行后可以在sqlplus中,或在SQLWindow窗口的Output中见到
员工ID:
1
工资月份:
200606
工资:
10000
触发器已执行
在代码的第一行,定义了数据库对象的类型是trigger,定义触发器的名称是salary_trg_rai
第二行说明了这是一个after触发器,在DML操作实施之后执行。
紧接着的insert说明了这是一个针对insert操作的触发器,每个对该表进行的insert操作都会执行这个触发器。
第三行说明了这是一个针对行级的触发器,当插入的记录有n条时,在每一条插入操作时都会执行该触发器,总共执行n次。
Declare后面跟的是本地变量定义部分,如果没有本地变量定义,此部分可以为空
Begin和end括起来的代码,是触发器的执行部分,一般会对插入记录进行一致性检查,在本例中打印了插入的记录和“触发器已执行”。
其中:
new对象表示了插入的记录,可以通过:
new.column_name来引用记录的每个字段值
触发器的语法如下
CREATEORREPLACETRIGGER
trigger_name
<
before|after|insteadof>
<
insert|update|delete>
ON
table_name
[FOREACHROW]
WHEN(condition)
DECLARE
BEGIN
--触发器代码
END;
Trigger_name是触发器的名称。
可以选择before或者after或insteadof。
Before表示在DML语句实施前执行触发器,而after表示在在dml语句实施之后执行触发器,insteadof触发器用在对视图的更新上。
可以选择一个或多个DML语句,如果选择多个,则用or分开,如:
insertorupdate。
Table_name是触发器关联的表名。
[FOREACHROW]为可选项,如果注明了FOREACHROW,则说明了该触发器是一个行级的触发器,DML语句处理每条记录都会执行触发器;
否则是一个语句级的触发器,每个DML语句触发一次。
WHEN后跟的condition是触发器的响应条件,只对行级触发器有效,当操作的记录满足condition时,触发器才被执行,否则不执行。
Condition中可以通过new对象和old对象(注意区别于前面的:
new和:
old,在代码中引用需要加上冒号)来引用操作的记录。
触发器代码可以包括三种类型:
未涉及数据库事务代码,涉及关联表(上文语法中的table_name)数据库事务代码,涉及除关联表之外数据库事务代码。
其中第一种类型代码只对数据进行简单运算和判断,没有DML语句,这种类型代码可以在所有的触发器中执行。
第二种类型代码涉及到对关联表的数据操作,比如查询关联表的总记录数或者往关联表中插入一条记录,该类型代码只能在语句级触发器中使用,如果在行级触发器中使用,将会报ORA-04091错误。
第三种类型代码涉及到除关联表之外的数据库事务,这种代码可以在所有触发器中使用。
从触发器的功能上来看,可以分成3类:
●
重写列(仅限于before触发器)
采取行动(任何触发器)
拒绝事务(任何触发器)
“重写列”用于对表字段的校验,当插入值为空或者插入值不符合要求,则触发器用缺省值或另外的值代替,在多数情况下与字段的default属性相同。
这种功能只能在行级before触发器中执行。
“采取行动”针对当前事务的特点,对相关表进行操作,比如根据当前表插入的记录更新其他表,银行中的总帐和分户帐间的总分关系就可以通过这种触发器功能来维护。
“拒绝事务”用在对数据的合法性检验上,当更新的数据不满足表或系统的一致性要求,则通过抛出异常的方式拒绝事务,在其上层的代码可以捕获这个异常并进行相应操作。
下面将通过举例说明,在例子中将触发器主体的语法一一介绍,读者可以在例子中体会触发器的功能。
CREATE
OR
REPLACE
TRIGGER
salary_raiu
AFTER
INSERT
UPDATE
OF
amount
ON
salary
FOR
EACH
ROW
IF
inserting
THEN
dbms_output.put_line(‘插入’);
ELSIF
updating
dbms_output.put_line(‘更新amount列’);
ENDIF;
以上是一个afterinsert和afterupdate的行级触发器。
在第二行中ofamountonsalary的意思是只有当amount列被更新时,update触发器才会有效。
所以,以下语句将不会执行触发器:
Updatesalarysetmonth=‘200601’wheremonth=‘200606’;
在触发器主体的if语句表达式中,inserting,updating和deleting可以用来区分当前是在做哪一种DML操作,可以作为把多个类似触发器合并在一个触发器中判别触发事件的属性。
新建员工表employment
TABLE
EMPLOYMENT
MAXSALARY
--工资上限
插入两条记录
Insertintoemploymentvalues(1,1000);
Insertintoemploymentvalues(2,2000);
WHEN
(
NEW.amount>
=
1000
AND
(old.amount
IS
NULL
OLD.amount<
500))
v_maxsalary
NUMBER;
SELECT
maxsalary
INTO
v_maxsalary
FROM
employment
WHERE
employee_id=:
NEW.employee_id;
:
raise_application_error(-20000,
'
工资超限'
);
END
IF;
以上的例子引入了一个新的表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包含两个参数,前一个是自定义错误代码,后一个是自定义错误代码信息。
其中自定义错误代码必须小于或等于-20000。
执行完该语句后,一个异常被抛出,如果在上一层有exception子句,该异常将被捕获。
如下面代码:
code
msg
VARCHAR2(500);
salary(employee_id,amount)
VALUES
(2,
5000);
EXCEPTION
OTHERS
code:
SQLCODE;
=substr(SQLERRM,
1,
500);
dbms_output.put_line(code);
dbms_output.put_line(msg);
执行后,将在output中或者sqlplus窗口中见着以下信息:
-20000
ORA-20000:
工资超出限制
ORA-06512:
在"
SCOTT.SALARY_RAI"
line9
ORA-04088:
触发器
SCOTT.SALARY_RAI'
执行过程中出错
这里的raise_application_error相当于拒绝了插入或者修改事务,当上层代码接受到这个异常后,判断该异常代码等于-20000,可以作出回滚事务或者继续其他事务的处理。
以上两个例子中用到的inserting,updating,deleting和raise_application_error都是dbms_standard包中的函数,具体的说明可以参照Oracle的帮助文档。
or
replace
package
sys.dbms_standard
is
procedure
raise_application_error(num
binary_integer,msg
varchar2,
function
return
boolean;
deleting
updating(colnam
varchar2)
end;
对于before和after行级触发器,:
old对象的属性值都是一样的,主要是对于在Oracle约束(Constraint)之前或之后的执行触发器的选择。
需要注意的是,可以在before行触发器中更改:
new对象中的值,但是在after行触发器就不行。
下面介绍一种insteadof触发器,该触发器主要使用在对视图的更新上,以下是insteadof触发器的语法:
INSTEADOF<
view_name
其他部分语法同前面所述的before和after语法是一样的,唯一不同的是在第二行用上了insteadof关键字。
对于普通的视图来说,进行insert等操作是被禁止的,因为Oracle无法知道操作的字段具体是哪个表中的字段。
但我们可以通过建立insteadof触发器,在触发器主体中告诉Oracle应该更新,删除或者修改哪些表的哪部分字段。
如:
新建视图
VIEW
employee_salary(employee_id,maxsalary,
MONTH,amount)
AS
a.employee_id,a.maxsalary,b.MONTH,b.amount
employmenta,salaryb
a.employee_id=b.employee_id
如果执行插入语句
MONTH,amount)
VALUES(10,
100000,
200606'
10000);
系统会报错:
ORA-01779:
无法修改与非键值保存表对应的列
我们可以通过建立以下的insteadof存储过程,将插入视图的值分别插入到两个表中:
trigger
employee_salary_rii
instead
of
insert
on
employee_salary
for
each
v_cnt
--检查是否存在该员工信息
COUNT(*)
v_cnt
v_cnt=
0
(employee_id,maxsalary)
VALUES
(:
NEW.employee_id,:
NEW.maxsalary);
--检查是否存在该员工的工资信息
NEW.employee_id
=:
NEW.MONTH;
(employee_id,
NEW.MONTH,:
NEW.amount);
employee_salary_rii;
该触发器被建立后,执行上述insert操作,系统就会提示成功插入一条记录。
但需要注意的是,这里的“成功插入一条记录”,只是Oracle并未发现触发器中有异常抛出,而根据insert语句中涉及的记录数作出一个判断。
若触发器的主体什么都没有,只是一个空语句,Oracle也会报“成功插入一条记录”。
同样道理,即使在触发器主体里往多个表中插入十条记录,Oracle的返回也是“成功插入一条记录”。
行级触发器可以解决大部分的问题,但是如果需要对本表进行扫描检查,比如要检查总的工资是否超限了,用行级触发器是不行的,因为行级触发器主体中不能有涉及到关联表的事务,这时就需要用到语句级触发器。
以下是语句级触发器的语法:
before|after|insteadof>
insert|update|delete>
--触发器主体
从语法定义上来看,行级触发器少了foreachrow,也不能使用when子句来限定入口条件,其他部分都是一样的,包括insert,update,delete和insteadof都可以使用。
salary_saiu
v_sumsalary
SUM(amount)
v_sumsalary
salary;
v_sumsalary>
500000
raise_application_error(-20001,
总工资超过500000'
以上代码定义了一个语句级触发器,该触发器检查在insert和update了amount字段后操作后,工资表中所有工资记录累加起来是否超过500000,如果超过则抛出异常。
从这个例子可以看出,语句级触发器可以对关联表表进行扫描,扫描得到的结果可以用来作为判断一致性的标志。
需要注意的是,在before语句触发器主体和after语句触发器主体中对关联表进行扫描,结果是不一样的。
在before语句触发器主体中扫描,扫描结果将不包括新插入和更新的记录,也就是说当以上代码换成before触发器后,以下语句将不报错:
salary(employee_id,
month,amount)
VALUEs(2,
200601'
600000)
这是因为在主体中得到的v_sumsalary并不包括新插入的600000工资。
另外,在语句级触发器中不能使用:
old对象,这一点和行级触发器是显著不同的。
如果需要检查插入或更新后的记录,可以采用临时表技术。
临时表是一种Oracle数据库对象,其特点是当创建数据的进程结束后,进程所创建的数据也随之清除。
进程与进程不可以互相访问同一临时表中对方的数据,而且对临时表进行操作也不产生undo日志,减少了数据库的消耗。
具体有关临时表的知识,可以参看有关书籍。
为了在语句级触发器中访问新插入后修改后的记录,可以增加行级触发器,将更新的记录插入临时表中,然后在语句级触发器中扫描临时表,获得修改后的记录。
临时表的表结构一般与关联表的结构一致。
目的:
限制每个员工的总工资不能超过50000,否则停止对该表操作。
创建临时表
global
temporary
SALARY_TMP
NUMBER,
VARCHAR2(6),
NUMBER
commit
delete
rows;
为了把操作记录插入到临时表中,创建行级触发器:
salary_tmp(employee_id,
month,amount)
VALUES(:
该触发器的作用是把更新后的记录信息插入到临时表中,如果更新了多条记录,则每条记录都会保存在临时表中。
创建语句级触发器:
salary_sai
am
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Oracle 触发器 使用 教程 命名 规范 V10