oracle数据库开发与应用 触发器 18

oracle数据库开发与应用 触发器 18

1
以前对数据进行操作,都是直接对数据表进行操作,只需要遵守数据表自身的约束条件就可以了。但是,实际工程中的数据操作,其对数据完整性要求的复杂度远远不止于此。
1
2
3
4
5
6
7
例如,在数据库中,课程表内要插入一门课程,但是要求这门课程的任课教师不是符传谊。
课程信息表结构如图18-1所示。
按照常理,首先要在TTEACHER中查询符传谊的教师编号

然后,使用约束来限制插入课程的任课教师编号。但是,查询得到结果来对数据表进行约束,需要手工去控制,非常麻烦,并且易出现错误。
在之前的讲解中,介绍了表的约束CHECK。可以看出,在课程信息表中存储了教师编号,但是没有存储教师的姓名,而CHECK只能对本表中的列进行判断,无法到别的表中进行判断。
于是,可以用到触发器

使用触发器

1
2
3
4
5
触发器(TRIGGER),能够对数据操作、数据定义、系统事件进行校验、监控、记录,从而保证数据的完整性、提高数据库安全性。

触发器的概念中,最重要的原理是“触发”,即,当发生指定的事件时,系统就会自动运行相应的程序块。

以上概念的潜台词是触发器不能手工调用,只能在某件事情(如添加、删除或修改)发生时,自动调用。
1
2
3
4
5
6
7
8
最简单的触发器,定义语法如下:
CREATE OR REPLACE TRIGGER 触发器名称BEFORE/AFTER INSERT/UPDATE/DELETE ON 表名/视图名
FOR EACH ROW
DECLARE
--变量定义;
BEGIN
--代码;
END;
1
2
3
其中,BEFORE/AFTER:表示触发器触发时机。INSERT/UPDATE/DELETE:定义触发器面向的操作。比如,写INSERT,表示触发器在 INSERT操作前(或后)触发。当然,此操作可以定义为多种操作。对多种操作进行触发,方法是各种操作用OR关键字隔开。

FOR EACH ROW:表示此触发器为行级触发器,对表中每一行进行操作。

image-20240525185416262

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1)关于:NEW.TEANO。
触发器运行时,数据还没有存入数据库,如何在触发器中得到即将插入的记录的各个字段的值?

在行级触发器中,经常需要使用对应操作记录字段的值,例如INSERT操作中插入前记录字段的值;UPDATE操作中修改前后记录字段的值;DELETE操作中删除前的记录字段的值。
在 Oracle 底层对数据操作前会对数据建暂态的内存记录,记录名默认值是:OLD和:NEW。其中,OLD表示数据被操作前记录的内容,NEW表示数据操作后记录的内容。例如,此处的:NEW.TEANO表示即将插入的记录中的TEANO。

2)关于RAISE_APPLICATION_ERROR(-20000,'该课程有错,任课教师不能为符传谊');。

在触发器中,经常要使用:当发现检查无法通过时,需要终止本次DML操作。

根据之前的知识,首先想到的可能是Oracle的事务控制,例如ROLLBACK。但是在触发器中,不能显式调用事务控制语句COMMIT和ROLLBACK,因为这些控制语句应该是在触发器操作之后由用户调用的。

要在触发器中实现中止DML操作,可以在触发器中使用下面这条语句:
RAISE_APPLICATION_ERROR(错误号,错误消息);

这条语句是在触发器中生成一个错误,系统得到这个错误后,会将本次操作回滚,并返回给用户错误号和错误消息。
其中,错误号一般是一个-20999~-20000的整数;错误消息是一个字符串。

3)如果触发器内部有语法错误,创建时不会提示,但是会在相应的触发器上打叉

所以,创建了触发器之后,一定要看看有没有创建成功。

测试触发器

1
2
3
4
5
6
7
接下来测试触发器。向表TCOURSE中插人一门课程,任课教师为符传谊
INSERT INTO T COURSE
VALUES('C021','电磁场理论','T001');
其中,T001是符传谊的TEANO。运行结果如图18-6所示此时,系统提示错误,单击Cancel按钮,查询表TCOURSE中所有记录:

结果表明,系统插人数据失败,这样就完成了18.1.2节的例子。
这就是触发器使用最基本、最常见的例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
在Oracle中有三类触发器:
数据操作语言(DML)触发器。针对DML语句,主要用于对数据操作进行校验或者引发级联操作。
数据定义语言(DDL)触发器。针对DDL语句,主要用于对数据定义进行权限控制。

系统事件触发器。针对系统事件,主要用于监视数据库系统的使用,并记录日志。

在工程中,使用最多的是数据操作语言触发器。

触发器有两种状态,可用与禁用,修改触发器状态:
ALTER TRIGGER 触发器名称 DISABL|ENABLE;

触发器能避免使用时尽量不使用,因为触发器本身比较耗费资源。删除触发器的语法为:
DROP TRIGGER 触发器名称;

数据操作语言触发器

1
2
3
4
5
6
7
8
9
10
11
12
数据操作语言最常用的是INSERT、DELETE和UPDATE语句。由于数据操作语言面向的对象是表或者视图,所以数据操作语言触发器作用的对象是表或者视图。根据触发器面向的操作,触发器分为3种:
INSERT 时自动触发的触发器。
DELETE 时自动触发的触发器。
UPDATE时自动触发的触发器

根据触发器触发的时机,触发器分为两种:
在数据增删改之前触发的BEFORE触发器
在数据增删改之后触发的AFTER触发器:

根据触发器作用的范围,触发器分为两种:
行级触发器。对表中每一行进行操作,都会触发这个触发器。
语句级触发器。对数据操作的每一条语句,都会触发这个触发器

用BEFORE触发器进行数据校验

1
2
3
行级触发器的特点是对表中每一行进行操作,都会触发这个触发器。如果一条语句对多行记录进行操作,就会多次触发这个触发器。

级触发器的第一个作用是数据操作前对数据进行校验,此时一般使用BEFORE触发器。

image-20240525191357213

image-20240525191611743

image-20240525191841872

1
2
UPDATE中数据的校验不仅可以对修改以后的数据进行校验,也可以校验修改
前的数据。

用 AFTER触发器进行级联操作

1
行级触发器的第二个作用是数据操作后进行级联操作,此时一般使用AFTER触发器。

image-20240525192800224

1
实际上,触发器的触发时机BEFORE/AFTER对触发器的执行影响不大,不过为了提高运行效率、便于理解,建议使用和业务逻辑相符的触发时机。触发器的使用可能会使得整个数据库逻辑复杂,特别是级联操作,很可能造成数据库混乱,所以在实际工程中,使用时一定要谨慎。

语句级触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
语句级触发器的特点是对数据操作的每一条语句,都会触发这个触发器,一条语句可能对多行记录进行操作,但是只触发一次这个触发器。

CREATE OR REPLACE TRIGGER 触发器名称
BEFORE/AFTER INSERT/UPDATE/DELETE
ON表名/视图名
DECLARE
--定义变量
BEGIN
--代码
END;

可以看出,语句级触发器的语法与行级触发器的语法基本相同发器少了。
FOR EACH ROW
限制数据操作

image-20240525234625683

记录操作日志
1
2
阶段性作业
建立一个操作日志表TLOGGER,如果对TSTUDENT表进行DML操作时,记录操作时间。

INSTEAD OF触发器

1
在数据操作语言触发器中,还有一类特殊的触发器,叫做INSTEAD OF触发器(替代触发器)。之所以说它特殊,是因为之前讲到的数据操作语言触发器面向的对象是表或者视图,但是INSTEAD OF触发器只能应用于视图。
1
在讲解之前,首先来新建一个视图,保存每个教师的姓名和他们讲授的课程名称。

image-20240525235251906

1
2
3
该视图使得课程名和教师姓名保存在同一个视图中,可以很方便地查询出这两个字段的对应关系。查询该视图,内容如图18-16所示。

在Oracle中,可以对视图进行DML操作,对应的操作会映射到基表中。但是,对于含有连接或子查询的视图,一般无法进行DML操作。

image-20240525235407117

1
这是因为:视图中的两个字段并不能保证是具有唯一性(或主键)约束的,如果直接操作,可能会造成同时对多行记录进行操作。所以在racle中,为了保证数据的一致性,对多表连接形成的视图,是不允许直接进行DML操作的。
1
2
3
4
5
6
7
8
9
10
11
12
13
INSTEAD OF触发器可以解决这个问题

INSTEAD OF触发器的基本语法为:

CREATE OR REPLACE TRIGGER 触发器名称
INSTEAD OF INSERT/UPDATE/DELETE
ON 视图名
FOR EACH ROW
DECLARE
--变量定义;
BEGIN
--代码;
END;

image-20240525235737568

image-20240525235752279

1
INSTEAD OF触发器一旦触发,就会执行触发器中的代码,而本身的11DML操作将不会执行,这就是为什么它名为 INSTEAD OF 触发器的原因。

数据定义语言触发器

1
2
3
4
5
6
7
8
9
10
11
数据定义语言(DDL)触发器主要用于监视数据库中用户的一些重要操作,如表建立、删除等。

数据定义语言触发器的基本语法为:
CREATE OR REPLACE TRIGGER 触发器名称
BEFORE/AFTER 用户事件
ON DATABASE|SCHEMA
DECLARE
--变量定义;
BEGIN
--代码;
END ;
1
这里的用户事件主要包括CREATE、ALTER、DROP等命令、用户登录LOGIN等。

image-20240526003838093

触发器和其他数据库对象的关系

1
2
触发器也可以调用存储过程,实际上,触发器是一种特殊的存储过程,它的结构函数和过程基本相同,但是它又有自己的特点:
函数和存储过程都需要显式的调用,但是触发器是隐式调用的,当发生指定的事件时,不需要手动调用,系统会自动调用相应的代码。函数和存储过程都可以接受参数,但是触发器不能接受参数。