DM 是一个具有主动特征的数据库管理系统,其主动特征包括约束机制和触发器机制。约束机制主要用于对某些列进行有效性和完整性验证;触发器(TRIGGER)定义当某些与数据库有关的事件发生时,数据库应该采取的操作。通过触发器机制,用户可以定义、删除和修改触发器。DM 自动管理和运行这些触发器,从而体现系统的主动性,方便用户使用。
触发器是一种特殊的存储过程,它在创建后就存储在数据库中。触发器的特殊性在于它是建立在某个具体的表或视图之上的,或者是建立在各种事件前后的,而且是自动激发执行的,如果用户在这个表上执行了某个 DML 操作(INSERT、DELETE、UPDATE),触发器就被激发执行触发器常用于自动完成一些数据库的维护工作。例如,触发器可以具有以下功能:
1. 可以对表自动进行复杂的安全性、完整性检查;
2. 可以在对表进行 DML 操作之前或者之后进行其它处理;
3. 进行审计,可以对表上的操作进行跟踪;
4. 实现不同节点间数据库的同步更新。
触发器与存储模块类似,都是在服务器上保存并执行的一段 DMSQL 程序语句。不同的是,存储模块必须被显式地调用执行,而触发器是在相关的事件发生时由服务器自动隐式地激发。触发器是激发它们的语句的一个组成部分,即直到一个语句激发的所有触发器执行完成之后该语句才结束,而其中任何一个触发器执行的失败都将导致该语句的失败,触发器所做的任何工作都属于激发该触发器的语句。
触发器为用户提供了一种自己扩展数据库功能的方法。可以使用触发器来扩充引用完整性,实施附加的安全性或增强可用的审计选项。关于触发器应用的例子有:
1. 利用触发器实现表约束机制(如:PRIMARY KEY、FOREIGN KEY、CHECK 等)无法实现的复杂的引用完整性;
2. 利用触发器实现复杂的事务规则(如:想确保薪水增加量不超过 25%);
3. 利用触发器维护复杂的缺省值(如:条件缺省);
4. 利用触发器实现复杂的审计功能;
5. 利用触发器防止非法的操作。
触发器是应用程序分割技术的一个基本组成部分,它将事务规则从应用程序的代码中移到数据库中,从而可确保加强这些事务规则并提高它们的性能。
DM 提供了四种类型的触发器:
1. 表级触发器:基于表中的数据进行触发;
2. 事件触发器:基于特定系统事件进行触发;
3. 时间触发器:基于时间而进行触发。
1.1 触发器的使用
触发器是依附于某个具体的表或视图的特殊存储过程,它在某个 DML 操作的激发下自动执行。在创建触发器时应该仔细考虑它的相关信息。具体来说,应该考虑以下几个方面的问题:
1. 触发器应该建立在哪个表/视图之上;
2. 触发器应该对什么样的 DML 操作进行响应;
3. 触发器在指定的 DML 操作之前激发还是在之后激发;
4. 对每次 DML 响应一次,还是对受 DML 操作影响的每一行数据都响应一次。
在确定了触发器的实现细节后,现在就可以创建触发器了,创建触发器的语法格式为:
CREATE [OR REPLACE] TRIGGER 触发器名[WITH ENCRYPTION]
BEFORE|AFTER|INSTEAD OF
DELETE|INSERT|UPDATE [OF 列名]
ON 表名
[FOR EACH ROW [WHEN 条件]]
BEGIN
DMSQL 程序语句
END;
用户如果要在自己的模式中创建触发器,需要具有 CREATE TRIGGER 数据库权限。
如果希望能够在其它用户的模式中创建触发器,需要具有 CREATE ANY TRIGGER 数据库权限。
在创建触发器的语法结构中,用方括号限定的部分是可选的,可以根据需要选用。创建触发器的命令是 CREATE TRIGGER,根据指定的名字创建一个触发器。OR REPLACE 子句的作用是如果已经存在同名的触发器,则删除它,并重新创建。
用 BEFORE 关键字创建的触发器是前激发的,用 AFTER 关键字创建的触发器是后激发的,这两个关键字只能使用其一。INSTEAD OF 子句仅用于视图上的触发器,表示用触发器体内定义的操作代替原操作。
触发器可以被任何 DML 命令激发,包括 INSERT、DELETE、UPDATE。如果希望其中的一种、两种或者三种命令能够激发该触发器,则可以指定它们之间的任意组合,两种不同的命令之间用 OR 分开。如果指定了 UPDATE 命令,还可以进一步指定当表中的哪个列受到UPDATE 命令的影响时激发该触发器。
FOR EACH ROW 子句的作用是指定创建的触发器为元组级触发器。如果没有这样的子句,则创建触发器为语句级触发器。INSTEAD OF 触发器固定为元组级触发器。
由关键字 BEGIN 和 END 限定的部分是触发器的代码,也就是触发器被激发时所执行的代码。代码的编写方法与普通的 DMSQL 语句块的编写方法相同。
在触发器中可以定义变量,但必须以 DECLARE 开头。触发器也可以进行异常处理,如果发生异常,就执行相应的异常处理程序。
例如,下面创建的触发器是为了监视用户对表 emp 中的数据所进行的删除操作。如果有这样的访问,则打印相应的信息。
CREATE OR REPLACE TRIGGER DEL_TRG
BEFORE DELETE
ON emp
BEGIN
PRINT '您正在对表 emp 进行删除操作';
END;
如果在表 EMP 上进行 DELETE 操作,则激发这个触发器,例如:
SQL> DELETE FROM EMP;
您正在对表 EMP 进行删除操作
从触发器的执行情况可以看出,无论用户通过 DELETE 命令删除 0 行、1 行或者多行数据,这个触发器只对每次 DELETE 操作激发一次,所以这是一个典型的语句级触发器。
如果一个触发器不再使用,那么可以删除它。删除触发器的语法为:
DROP TRIGGER 触发器名;
例如,要删除刚才创建的触发器 DEL_TRG,使用语句为:
SQL> DROP TRIGGER DEL_TRG;
触发器的创建者和数据库管理员可以使触发器失效。触发器失效后将暂时不起作用,直到再次使它有效,使触发器失效的命令格式为:
ALTER TRIGGER 触发器名 DISABLE;
触发器失效后只是暂时不起作用,它仍然存在于数据库中,使用命令可以使它再次起作用,使触发器再次有效的命令格式为:
ALTER TRIGGER 触发器 ENABLE;
例如,下面的两条命令先使触发器 DEL_TRG 失效,然后使其再次有效:
SQL> ALTER TRIGGER DEL_TRG DISABLE;
SQL> ALTER TRIGGER DEL_TRG ENABLE;
1.2 表级触发器
表级触发器都是基于表中数据的触发器,它通过针对相应表对象的插入/删除/修改等DML 语句触发。
n 触发动作
激发表级触发器的触发动作是三种数据操作命令,即 INSERT、DELETE 和 UPDATE 操作。在触发器定义语句中用关键字 INSERT、DELETE 和 UPDATE 指明构成一个触发器事件的数据操作的类型,其中 UPDATE 触发器会依赖于所修改的列,在定义中可通过 UPDATE OF<触发列清单>的形式来指定所修改的列,<触发列清单>指定的字段数不能超过 128 个。
n 触发级别
根据触发器的级别可分为元组级 (也称行级)和语句级。
元组级触发器,对触发命令所影响的每一条记录都激发一次。假如一个 DELETE 命令从表中删除了 1000 行记录,那么这个表上的元组级 DELETE 触发器将被执行 1000 次。
元组级触发器常用于数据审计、完整性检查等应用中。元组级触发器是在触发器定义语句中通过 FOR EACH ROW 子句创建的。对于元组级触发器,可以用一个 WHEN 子句来限制针对当前记录是否执行该触发器。WHEN 子句包含一条布尔表达式,当它的值为 TRUE 时,执行触发器;否则,跳过该触发器。
语句级触发器,对每个触发命令执行一次。例如,对于一条将500行记录插入表TABLE_1中的 INSERT 语句,这个表上的语句级 INSERT 触发器只执行一次。语句级触发器一般用于对表上执行的操作类型引入附加的安全措施。语句级触发器是在触发器定义语句中通过 FOR EACH STATEMENT 子句创建的,该子句可缺省。
n 触发时机
触发时机通过两种方式指定。一是通过指定 BEFORE 或 AFTER 关键字,选择在触发动作之前或之后运行触发器;二是通过指定 INSTEAD OF 关键字,选择在动作触发的时候,替换原始操作,INSTEAD OF 允许建立在视图上,并且只支持行级触发。
在元组级触发器中可以引用当前修改的记录在修改前后的值,修改前的值称为旧值,修改后的值称为新值。对于插入操作不存在旧值,而对于删除操作则不存在新值。
对于新、旧值的访问请求常常决定一个触发器是 BEFORE 类型还是 AFTER 类型。如果需要通过触发器对插入的行设置列值,那么为了能设置新值,需要使用一个 BEFORE 触发器,因为在 AFTER 触发器中不允许用户设置已插入的值。在审计应用中则经常使用 AFTER 触发器,因为元组修改成功后才有必要运行触发器,而成功地完成修改意味着成功地通过了该表的引用完整性约束。
1.3 事件触发器
前面表级触发器都是基于表中数据的触发器,它通过针对相应表对象的插入/删除/修改等 DML 语句触发。DM 还支持事件触发器,包括库级和模式级触发器,这类触发器并不依赖于某个表,而是基于特定系统事件触发的,通过指定 DATABASE 或某个 SCHEMA 来表示事件触发器的作用区域。创建事件触发器的用户需要拥有 CREATE_TRIGGER 或 CREATE_ANY_TRIGGER 的权限。
可以触发的事件包含以下两类:
1. DDL 事件,包括 CREATE、ALTER、DROP、GRANT、REVOKE 以及 TRUNCATE;
2. 系统事件,包括 LOGIN/LOGON、LOGOUT/LOGOFF、AUDIT、NOAUDIT、BACKUP DATABASE、RESTORE DATABASE、TIMER、STARTUP、SHUTDOWN 以及 SERERR(即执行错误事件)。
所有DDL事件触发器都可以设置BEFORE或AFTER的触发时机,但系统事件中LOGOUT,SHUTDOWN 仅能设置为 BEFORE,而其它则只能设置为 AFTER。模式级触发器不能是LOGIN/LOGON、LOGOUT/LOGOFF、SERERR、BACKUP DATABASE、RESTORE DATABASE、STARTUP 和 SHUTDOWN 事件触发器。
与数据触发器不同,事件触发器不能影响对应触发事件的执行。它的主要作用是帮助管理员监控系统运行发生的各类事件,进行一定程度的审计和监视工作。
1.4 时间触发器
时间触发器是一种特殊的事件触发器。时间触发器的特点是用户可以定义在任何时间点、时间区域、每隔多长时间等等的方式来激发触发器,而不是通过数据库中的某些操作包括DML、DDL 操作等来激发,它的最小时间精度为分钟。
时间触发器与其它触发器的不同只是在触发事件上,在 DMSQL 语句块(BEGIN 和 END之间的语句)的定义是完全相同的。
时间触发器的创建语句如下:
CREATE [OR REPLACE] TRIGGER 触发器名 WITH ENCRYPTION
AFTER TIMER ON DATABASE
{时间定义语句}
BEGIN
执行语句
END;
时间定义语句的语法规则较多,具体可参考《DM8_SQL 语言使用手册》中时间触发器相关章节。
下面的例子在每个月的第 28 天,从早上 9 点开始到晚上 18 点之间,每隔一分钟就打印一个字符串“Hello World”。
CREATE OR REPLACE TRIGGER timer2
AFTER TIMER ON DATABASE
FOR EACH 1 MONTH DAY 28
FROM TIME '09:00' TO TIME '18:00' FOR EACH 1 MINUTE
DECLARE
str VARCHAR;
BEGIN
PRINT 'HELLO WORLD';
END;
/
时间触发器实用性很强,如在凌晨(此时服务器的负荷比较轻)做一些数据的备份操作,对数据库中表的统计信息的更新操作等类似的事情。同时也可以作为定时器通知一些用户在未来的某些时间要做哪些事情。
1.5 触发器总结
表级触发器的触发事件包括某个基表上的 INSERT、DELETE 和 UPDATE 操作,无论对于哪种操作,都能够为其创建 BEFORE 触发器和 AFTER 触发器。如果触发器的动作代码不取决于受影响的数据,语句级触发器就非常有用。例如,可以在表上创建一个 BEFORE INSERT 语句触发器,以防止在某些特定期限以外的时间对一个表进行插入。
每张基表上可创建的触发器的个数没有限制,但是触发器的个数越多,处理 DML 语句所需的时间就越长,这是显而易见的。创建触发器的用户必须是基表的创建者,或者拥有 DBA权限。注意,不存在触发器的执行权限,因为用户不能主动“调用”某个触发器,是否激发一个触发器是由系统来决定的。
对于语句级和元组级的触发器来说,都是在 DML 语句运行时激发的。在执行 DML 语句的过程中,基表上所创建的触发器按照下面的次序依次执行:
1. 如果有语句级前触发器的话,先运行该触发器;
2. 对于受语句影响每一行:
1) 如果有行级前触发器的话,运行该触发器;
2) 执行该语句本身;
3) 如果有行级后触发器的话,运行该触发器;
3. 如果有语句级后触发器的话,运行该触发器。
需要注意的是,同类触发器的激发顺序没有明确的定义。如果顺序非常重要的话,应该把所有的操作组合在一个触发器中。触发器功能强大,但需要谨慎使用,过多的触发器或复杂触发器过程脚本会降低数据库的运行效率。
最后一点,在 DM 的数据守护环境下,备库上定义的触发器是不会被触发的。