一个组合触发器中可以包含行前、行后、语句前、语句后四种触发事件中的一到四种或只包含一个 INSTEAD OF 触发事件。
概述
在表或可编辑视图上创建的组合触发器可以在多个时间点触发。组合触发器按照每个时间点分隔成段,每个时间段都有自己的可执行部分和异常处理部分(可选的),所有这些部分都可以访问一个通用的 PL 状态。通用状态在触发语句开始时建立,在触发语句完成时销毁,即使触发语句导致错误时也不会受到影响。
组合触发器的使用限制
组合 DML 触发器具有以下限制:
OLD、NEW和PARENT不能出现在声明部分,也不能出现在BEFORE STATEMENT部分或AFTER STATEMENT部分。只有
BEFORE EACH ROW部分可以更改NEW的值。一个时间段不能处理在另一个时间段中引发的异常。
如果一个时间段内包含
GOTO语句,则GOTO语句的目标必须在同一时间段中。
创建触发器
在组合触发器的声明部分(可选),您可以声明其所有由时间点分隔的段都能使用的变量和子程序。当触发器被触发时,声明部分在任何时间段运行之前运行。变量和子程序在触发语句所作用的时间段内存在。
在不可编辑视图上创建的组合触发器并不是真正意义上的组合触发器,因为它只有一个时间段。在不可编辑视图上创建一个简单的组合触发器的语法如下:
CREATE TRIGGER FOR dml_event_clause ON view_name
COMPOUND TRIGGER
INSTEAD OF EACH ROW IS BEGIN
sql_statement;
END INSTEAD OF EACH ROW;
在表或可编辑视图上创建的组合触发器至少有一个时间段。如果触发器有多个时间段,它们可以按任意顺序排列,但时间点不能重复。如果时间段不存在,则在该时间点不会发生任何操作。
组合触发器主要有如下几个时间段:
| 时间节点 | 时间段 |
|---|---|
| 在触发语句运行之前 | BEFORE STATEMENT |
| 在触发语句运行后 | AFTER STATEMENT |
| 在触发语句影响的每一行之前 | BEFORE EACH ROW |
| 在触发语句影响的每一行之后 | AFTER EACH ROW |
组合触发器没有初始化部分,但是 BEFORE STATEMENT 可以进行任何所需的初始化,因为它先于其他时间段运行。
如果组合触发器既没有 BEFORE STATEMENT 部分,也没有 AFTER STATEMENT 部分,并且其触发语句不影响任何行,则触发器永远不会触发。
使用组合触发器将更改信息记录到一个表的子表中。示例如下:
CREATE TABLE emp_salaries (
emp_id NUMBER NOT NULL,
change_date DATE NOT NULL,
salary NUMBER(8,2) NOT NULL,
FOREIGN KEY (emp_id)
REFERENCES employees (employee_id) ON DELETE CASCADE);
obclient> delimiter /
obclient> CREATE OR REPLACE TRIGGER maintain_emp_salaries
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
threshold CONSTANT SIMPLE_INTEGER := 7;
TYPE salaries_t IS TABLE OF emp_salaries%ROWTYPE INDEX BY SIMPLE_INTEGER;
sal salaries_t;
idx SIMPLE_INTEGER := 0;
PROCEDURE flush_proc IS
n CONSTANT SIMPLE_INTEGER := sal.count();
BEGIN
FORALL j IN 1..n
INSERT INTO emp_salaries VALUES sal(j);
sal.delete();
idx := 0;
DBMS_OUTPUT.PUT_LINE('刷新' || n || '行');
END flush_proc;
-- AFTER EACH ROW 时间段:
AFTER EACH ROW IS
BEGIN
idx := idx + 1;
sal(idx).emp_id := :NEW.employee_id;
sal(idx).change_date := SYSTIMESTAMP;
sal(idx).salary := :NEW.salary;
IF idx >= threshold THEN
flush_proc();
END IF;
END AFTER EACH ROW;
-- AFTER STATEMENT 时间段:
AFTER STATEMENT IS
BEGIN
flush_proc();
END AFTER STATEMENT;
END maintain_emp_salaries;
/
Query OK, 0 rows affected
obclient> SET SERVEROUTPUT ON;
Query OK, 0 rows affected
/* 将部门 50 中每个员工的工资提高 8% */
obclient> UPDATE employees
SET salary = salary * 0.8
WHERE department_id = 50
/
Query OK, 10 rows affected
Rows matched: 10 Changed: 10 Warnings: 0
刷新7行
Flushed 7 rows
刷新3行
Flushed 3 rows
/* 等待 3 秒钟 */
obclient> BEGIN
DBMS_LOCK.SLEEP(3);
END
/
Query OK, 1 row affected(3.01 sec)
/* 将部门 50 中每个员工的工资提高 1.1% */
obclient> UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 50
/
Query OK, 10 rows affected
Rows matched: 10 Changed: 10 Warnings: 0
刷新7行
Flushed 7 rows
刷新3行
Flushed 3 rows
/* 查看 emp_salaries 表中的员工表的更改情况 */
obclient> SELECT emp_id, count(*) num
FROM emp_salaries
GROUP BY emp_id
/
+--------+------+
| EMP_ID | NUM |
+--------+------+
| 120 | 1 |
| 121 | 1 |
| 122 | 1 |
| 123 | 1 |
| 124 | 1 |
| 125 | 1 |
| 126 | 1 |
| 127 | 1 |
| 128 | 1 |
| 129 | 1 |
+--------+------+
10 rows in set



