一、触发器作用
(1)利用触发器实现表约束机制无法实现的复杂的引用完整性;
(2)利用触发器实现复杂的事务规则(如:想确保薪水增加量不超过 25%);
(3)利用触发器维护复杂的缺省值(如:条件缺省);
(4)利用触发器实现复杂的审计功能;
(5)利用触发器防止非法的操作。
二、触发器定义
触发器分为表触发器、事件触发器和时间触发器。
表触发器是对表里数据DML操作引发的数据库的触发。
事件触发器是对数据库对象操作引起的数据库的触发。
时间触发器是一种特殊的事件触发器。
三、表触发器
3.1、语法说明
CREATE [OR REPLACE] TRIGGER [<模式名>.]<触发器名> [WITH ENCRYPTION]
<触发限制描述>
[REFERENCING ][REFERENCING <trig_referencing_list>]
[FOR EACH {ROW | STATEMENT}]
[WHEN (<条件表达式>)]
<触发器体>
<trig_referencing_list>::= <referencing_1>|<referencing_2>
<referencing_1>::=OLD [ROW] [AS] <引用变量名> [ NEW [ROW] [AS] <引用变量名>]
<referencing_2>::=NEW [ROW] [AS] <引用变量名>
<触发限制描述>::=<触发限制描述1> | <触发限制描述2>
<触发限制描述1>::= <BEFORE|AFTER> <触发事件列表> [LOCAL] ON <触发表名>
<触发限制描述2>::= INSTEAD OF <触发事件列表> [LOCAL] ON <触发视图名>
<触发表名>::=[<模式名>.]<基表名>
<触发事件>::=INSERT|DELETE|{UPDATE|{UPDATE OF<触发列清单>}}
<触发事件列表>::=<触发事件> | {<触发事件列表> OR <触发事件>}
语法说明:
1.< 触发器名 > 指明被创建的触发器的名称;
2.BEFORE 指明触发器在执行触发语句之前激发;
3.AFTER 指明触发器在执行触发语句之后激发;
4.INSTEAD OF 指明触发器执行时替换原始操作;
5.< 触发事件 > 指明激发触发器的事件。INSTEAD OF 中不支持 {UPDATE OF < 触发列清单 >};
6.< 基表名 > 指明被创建触发器的基表的名称;
7.WITH ENCRYPTION 选项,指定是否对触发器定义进行加密;
8.REFERENCING 子句 指明相关名称可以在元组级触发器的触发器体和 WHEN 子句中利用相关名称来访问当前行的新值或旧值,缺省的相关名称为 OLD 和 NEW;
9.< 引用变量名 > 标识符,指明行的新值或旧值的相关名称;
10.FOR EACH 子句 指明触发器为元组级或语句级触发器。FOR EACH ROW 表示为元组级触发器,它受被触发命令影响、且 WHEN 子句的表达式计算为真的每条记录激发一次。FOR EACH STATEMENT 为语句级触发器,它对每个触发命令执行一次。FOR EACH 子句缺省则为语句级触发器;
11.WHEN 子句 表触发器中只允许为元组级触发器指定 WHEN 子句,它包含一个布尔表达式,当表达式的值为 TRUE 时,执行触发器;否则,跳过该触发器;
12.< 触发器体 > 触发器被触发时执行的 SQL 过程语句块。
限制说明:
1、触发器名称不能与模式内的其他模式对象同名,并且不能使用“MTRG$_”作为前缀;
2、可以使用 OR REPLACE 选项来替换一个触发器,但是要注意被替换的触发器的触发表不能改变。如果要在同一模式内不同的表上重新创建一个同名的触发器,则必须先删除源触发器,然后再创建;
3、在一张基表上允许创建的表触发器的个数没有限制,一共允许有 12 种类型。它们分别是:BEFORE INSERT 行级、BEFORE INSERT 语句级、AFTER INSERT 行级、AFTER INSERT 语句级、BEFORE UPDATE 行级、BEFORE UPDATE 语句级、AFTER UPDATE 行级、AFTER UPDATE 语句级、BEFORE DELETE 行级、BEFORE DELETE 语句级、AFTER DELETE 行级和 AFTER DELETE 语句级;
4、触发器是在 DML 语句运行时激发的,执行 DML 语句的算法步骤如下:
Before语句级触发器-->before行触发器-->执行该语句本身-->after行触发器-->after语句触发器
5、INSTEAD OF 触发器只允许建立在视图上,并且只支持行级触发。
6、表触发器不支持跨模式,即 < 触发器名 > 必须和 < 触发表名 >、< 触发视图名 > 的模式名一致。若创建表触发器时不指定模式名,则模式名默认为触发表名或触发视图的模式同名。
7、水平分区子表、HUGE 表不支持表触发器;
8、在 MPP 环境下,执行 LOCAL 类型触发器时,会话会被临时变为 LOCAL 类型,因此触发器体只会在本节点执行,不会产生节点间的数据交互,触发器体中只能包含表的值插入操作,如果插入数据的目标节点不是于本节点,则会报错,随机分布表没有此限制。
3.2、触发事件(触发动作)
触发器的触发动作是三种数据操作命令,即 INSERT、DELETE 和 UPDATE 操作。所以也叫INSERT 触发器,update触发器,delete触发器,一个触发器的触发事件也可以是多个数据操作命令的组合,这时这个触发器可由多种数据操作命令触发。
案例:创建一个触发器,触发后向日志表写入信息
3.3、触发级别
分为行触发器(元组触发器)和语句触发器。
行触发器(元组级触发器):对触发命令所影响的每一条记录都激发一次。元组级触发器常用于数据审计、完整性检查等应用中。元组级触发器是在触发器定义语句中通过 FOR EACH ROW 子句创建的。对于元组级触发器,可以用一个 WHEN 子句来限制针对当前记录是否执行该触发器。WHEN 子句包含一条布尔表达式,当它的值为 TRUE 时,执行触发器;否则,跳过该触发器。
语句级触发器:对每个触发命令执行一次。语句级触发器一般用于对表上执行的操作类型引入附加的安全措施。语句级触发器是在触发器定义语句中通过 FOR EACH STATEMENT 子句创建的,该子句可缺省。
案例1:创建一个行触发器,带有when条件
3.4、触发时机
触发时机通过两种方式指定。
一是通过指定 BEFORE 或 AFTER 关键字,选择在触发动作之前或之后运行触发器;
二是通过指定 INSTEAD OF 关键字,选择在动作触发的时候,替换原始的触发操作,INSTEAD OF 允许建立在视图上,并且只支持行级触发。
在审计应用中则经常使用 AFTER 触发器,因为元组修改成功后才有必要运行触发器,而成功地完成修改意味着成功地通过了该表的引用完整性约束。
案例1:
如果需要通过触发器对插入的行设置列值,那么为了能设置新值,需要使用一个 BEFORE 触发器,因为在 AFTER 触发器中不允许用户设置已插入的值。
案例2:
案例3:instead of 操作触发器
3.5、新、旧行值的引用
在行触发器内部,可以访问正在处理中的记录的数据,这种访问是通过两个引用变量:OLD 和:NEW 实现的。:OLD 表示记录被处理前的值,:NEW 表示记录被处理后的值,标识符前面的冒号说明它们是宿主变量意义上的连接变量,而不是一般的 DMSQL 程序变量。我们还可以通过引用子句为这两个行值重新命名。引用变量与其它变量不在同一个命名空间,所以变量可以与引用变量同名。
在触发器体中使用引用变量时,必须采用下列形式::引用变量名.列名
列名必须是触发表中存在的列,否则编译器将报错。
:OLD 引用变量只能读取,不能赋值(因为设置这个值是没有任何意义的);而:NEW 引用变量则既可读取,又可赋值(当然必须在 BEFORE 类型的触发器中,因为数据操作完成后再设置这个值也是没有意义的)。通过修改:NEW 引用变量的值,我们可以影响插入或修改的数据。
详见触发时机中的案例2.
3.6、触发器谓词
当一个触发器可以为多个 DML 语句触发时,在这种触发器体内部可以使用三个谓词:INSERTING、DELETING 和 UPDATING 来确定当前执行的是何种操作。
INSERTING 当触发语句为 INSERT 时为真,否则为假;DELETING 当触发语句为 DELETE 时为真,否则为假;UPDATING[(< 列名 >)] 未指定列名时,当触发语句为 UPDATE 时为真,否则为假;指定某一列名时,当触发语句为对该列的 UPDATE 时为真,否则为假。
四、事件触发器
是在数据库全局对象上创建触发器。
- 语法:
CREATE [OR REPLACE] TRIGGER [<模式名>.]<触发器名> [WITH ENCRYPTION]
<BEFORE| AFTER> <触发事件子句> ON <触发对象名>
[EXECUTE AT <触发的RAFT组名><DMDSC节点号>]
[WHEN <条件表达式>]
<触发器体>
<触发事件子句>:=<DDL事件子句>| <系统事件子句>
<DDL事件子句>:=<DDL事件>{OR <DDL事件>}
<DDL事件>:=DDL|<CREATE|ALTER|DROP|GRANT|REVOKE|TRUNCATE|COMMENT>
<系统事件子句>:=<系统事件>{OR <系统事件>}
<系统事件>:= LOGIN|LOGOUT|SERERR|<BACKUP DATABASE>|<RESTORE DATABASE>|AUDIT|NOAUDIT|TIMER|STARTUP|SHUTDOWN|CHECKPOINT
<触发对象名>:=[<模式名>.]SCHEMA| DATABASE
(1)< 触发的 RAFT 组名 > :专门用于 DMDPC(非 DPC 环境直接忽略指定值),用于指定在 RAFT 组中的节点触发,不涉及的节点上不触发。其中,多副本的 MP RAFT 只在主库触发。缺省 < 触发的 RAFT 组名 > 时,由于 TIMER 触发器数量与复杂程度均不可控,默认选择 ID 最小的 SP 执行(如果无活动 SP 则选择 ID 最小的 BS),以减小对 MP 自身事务管理任务的干扰。< 触发的 RAFT 组名 > 指定为“RAFT_AT_ALL_SP$”时,MP 将触发器动态分发到各个 SP 执行,实现负载相对均衡。对确定在 MP 执行的触发器(如 DDL)忽略指定的 RAFT。BP 模式的节点上不触发任何触发器;
(2)< 触发事件子句 > 说明激发触发器的事件,DDL 事件以及系统事件。DDL 事件包括数据库和模式上的 DDL 操作;系统事件包括数据库上的除 DDL 操作以外系统事件;以上事件可以有多个,用 OR 列出。
(3)DDL 关键字的作用相当于 CREATE OR DROP OR ALTER OR TRUNCATE OR COMMENT。
(4)< 触发对象名 > 是触发事件发生的对象,DATABASE 和 < 模式名 > 只对 DDL 事件有效, < 模式名 > 可以缺省。创建模式触发器时,触发对象名直接用 SCHEMA;
(5)通过系统存储过程 SP_ENABLE_EVT_TRIGGER 和 SP_ENABLE_ALL_EVT_TRIGGER 可以禁用/启用指定的事件触发器或所有的事件触发器。
(6)对于事件触发器,所有的事件信息都通过伪变量 :EVENTINFO 来取得。
(7)创建的触发器可以分为以下几类:
在自己拥有的模式中创建自己模式的对象上的触发器或创建自己模式上的触发器;
在任意模式中创建任意模式的对象上的触发器或创建其他用户模式上(. SCHEMA)的触发器,即支持跨模式的触发器,表现为 < 触发器名 > 和 < 触发对象名 > 的 < 模式名 > 不同;
创建数据库上(DATABASE)的触发器。
案例1:登录一次数据库创建触发器
2、事件属性函数用法
当事件触发器被触发时,可以通过这些事件属性函数获取当前事件的属性。
针对用户设置的数据库事件(DDL 语句执行),获取事件触发时的相关属性。事件属性函数如下:
1、DM_DICT_OBJ_NAME,无参数,返回事件对象名;
2、DM_DICT_OBJ_TYPE,无参数,返回事件对象类型;
3、DM_DICT_OBJ_OWNER,无参数,返回事件对象所在模式;
4、DM_SQL_TXT,有 1 个输出参数,参数类型为 DM_NAME_LIST_T,返回值为 DDL 语句占用的嵌套表单元个数。DM_SQL_TXT 帮助用户获取事件被触发时正在执行的 DDL 语句,用于存储获取到的 DDL 语句。DM_NAME_LIST_T 为元素类型为 varchar(64)的嵌套表。因此如果 DDL 语句过长会导致分片存储,用户在获取 DDL 语句的时候,尤其要注意根据返回值来循环读取嵌套表以获取完整的语句。
五、时间触发器
时间触发器属于一种特殊的事件触发器,它使得用户可以定义一些有规律性执行的、定点执行的任务,比如在晚上服务器负荷轻的时候通过时间触发器做一些更新统计信息的操作、自动备份操作等等,因此时间触发器是非常有用的。相当于创建定时任务。
语法:
CREATE [OR REPLACE] TRIGGER [<模式名>.]<触发器名>[WITH ENCRYPTION]
AFTER TIMER ON DATABASE[EXECUTE AT <触发的RAFT组名>] <{FOR ONCE AT DATETIME [<时间表达式>]
<exec_ep_seqno>}|
{{<month_rate>|
<week_rate>|
<day_rate>} {<once_in_day>|<times_in_day>}{<during_date>}
<exec_ep_seqno>}>
[WHEN <条件表达式>]
<触发器体>
<month_rate>:= {FOR EACH <整型变量> MONTH {<day_in_month>}}| {FOR EACH <整型变量> MONTH { <day_in_month_week>}}
<day_in_month>:= DAY <整型变量>
<day_in_month_week>:= {DAY <整型变量> OF WEEK<整型变量>}|{DAY <整型变量> OF WEEK LAST}
<week_rate>:=FOR EACH <整型变量> WEEK {<day_of_week_list>}
<day_of_week_list >:= {<整型变量>}|{, <整型变量>}
<day_rate>: =FOR EACH <整型变量> DAY
<once_in_day >:= AT TIME <时间表达式>
<times_in_day >:={ <duaring_time> } FOR EACH <整型变量> <freq_sub_type>
<freq_sub_type>:= MINUTE | SECOND
<duaring_time>:={NULL}|{FROM TIME <时间表达式>}|{FROM TIME <时间表达式> TO TIME <时间表达式>}
<duaring_date>:={NULL}|{FROM DATETIME <日期时间表达式>}|{FROM DATETIME <日期时间表达式> TO DATETIME <日期时间表达式>}
<exec_ep_seqno>:=EXECUTE AT <DMDSC节点号>
案例:每分钟向日志表中插入一条数据。
社区地址: https://eco.dameng.com