1.作用
触发器(TRIGGER)定义为当某些与数据库有关的事件发生时,数据库应该采取的操作。
这些事件包括全局对象、数据库下某个模式、模式下某个基表上的 INSERT、DELETE 和
UPDATE 操作。触发器与存储模块类似,都是在服务器上保存并执行的一段 DMSQL 程序语
句。不同的是:存储模块必须被显式地调用执行,而触发器是在相关的事件发生时由服务器
自动地隐式地激发。触发器是激发它们的语句的一个组成部分,即直到一个语句激发的所有
触发器执行完成之后该语句才结束,而其中任何一个触发器执行的失败都将导致该语句的失
败,触发器所做的任何工作都属于激发该触发器的语句。
需要说明的是,在 DM 的数据守护环境下,备库上定义的触发器是不会被触发的。
2.定义与分类
触发器分为表触发器、事件触发器和时间触发器。表触发器是对表里数据操作引发的数
据库的触发;事件触发器是对数据库对象操作引起的数据库的触发;时间触发器是一种特殊
的事件触发器。
表触发器
激发表触发器的触发动作是三种数据操作命令,即 INSERT、DELETE 和 UPDATE 操作。
在触发器定义语句中用关键字 INSERT、DELETE 和 UPDATE 指明构成一个触发器事件的数据操作的类型,其中 UPDATE 触发器会依赖于所修改的列,在定义中可通过 UPDATE OF <触发列清单>的形式来指定所修改的列,<触发列清单>指定的字段数不能超过 128 个。
SQL> SET SERVEROUTPUT ON;
SQL> CREATE OR REPLACE TRIGGER INS_DEL_UPD_TRG
2 BEFORE INSERT OR UPDATE OR DELETE ON TEST1
3 BEGIN
4 PRINT 'THIS IS FORBIDDEN';
5 END;
6 /
操作已执行
已用时间: 49.888(毫秒). 执行号:2229.
SQL> INSERT INTO TEST1 VALUES (1,'LOU');
THIS IS FORBIDDEN
THIS IS FORBIDDEN
影响行数 1
已用时间: 0.859(毫秒). 执行号:2230.
SQL> SELECT * FROM TEST1;
行号 ID NAME
---------- ----------- ----
1 1 LOU
已用时间: 0.575(毫秒). 执行号:2232.
触发器触发,数据插入。该触发器对更新,删除同样生效。
根据触发器的级别可分为元组级 (也称行级)和语句级。
元组级触发器,对触发命令所影响的每一条记录都激发一次。假如一个 DELETE 命令
从表中删除了 1000 行记录,那么这个表上的元组级 DELETE 触发器将被执行 1000 次。元组级触发器常用于数据审计、完整性检查等应用中。元组级触发器是在触发器定义语句中通过 FOR EACH ROW 子句创建的。对于元组级触发器,可以用一个 WHEN 子句来限制针对当前记录是否执行该触发器。WHEN 子句包含一条布尔表达式,当它的值为 TRUE 时,执行触发器;否则,跳过该触发器。
SQL> CREATE OR REPLACE TRIGGER TRG_DEL_ROW
BEFORE DELETE OR INSERT ON TEST1
FOR EACH ROW
BEGIN
PRINT 'THIS IS FOR EACH ROW TEST';
END
2 3 4 5 6 7 /
操作已执行
已用时间: 33.321(毫秒). 执行号:2301.
SQL> DELETE FROM TEST1 WHERE ID=6;
THIS IS FOR EACH ROW TEST
THIS IS FOR EACH ROW TEST
影响行数 2
已用时间: 0.669(毫秒). 执行号:2306.
SQL> INSERT INTO TEST1 VALUES (7,'LUI');
THIS IS FOR EACH ROW TEST
THIS IS FOR EACH ROW TEST
影响行数 1
已用时间: 0.389(毫秒). 执行号:2307.
语句级触发器,对每个触发命令执行一次。例如,对于一条将 500 行记录插入表TABLE_1 中的 INSERT 语句,这个表上的语句级 INSERT 触发器只执行一次。语句级触发器一般用于对表上执行的操作类型引入附加的安全措施。语句级触发器是在触发器定义语句中通过 FOR EACH STATEMENT 子句创建的,该子句可缺省。
SQL> CREATE OR REPLACE TRIGGER TRG_INS_DEL_ST
AFTER INSERT OR DELETE ON TEST1
FOR EACH STATEMENT
BEGIN
PRINT '语句级触发器展示';
END
/
操作已执行
已用时间: 12.901(毫秒). 执行号:2418.
SQL> DELETE FROM TEST1 WHERE ID=8;
语句级触发器展示
影响行数 0
已用时间: 0.767(毫秒). 执行号:2421.
SQL> INSERT INTO TEST1 VALUES (1,'aghagj');
语句级触发器展示
影响行数 1
已用时间: 0.411(毫秒). 执行号:2422.
触发时机通过两种方式指定。一是通过指定 BEFORE 或 AFTER 关键字,选择在触发动作之前或之后运行触发器;二是通过指定 INSTEAD OF 关键字,选择在动作触发的时候,替换原始操作,INSTEAD OF 允许建立在视图上,并且只支持行级触发。
在元组级触发器中可以引用当前修改的记录在修改前后的值,修改前的值称为旧值,修改后的值称为新值。对于插入操作不存在旧值,而对于删除操作则不存在新值。
对于新、旧值的访问请求常常决定一个触发器是 BEFORE 类型还是 AFTER 类型。如果需要通过触发器对插入的行设置列值,那么为了能设置新值,需要使用一个 BEFORE 触发器,因为在 AFTER 触发器中不允许用户设置已插入的值。在审计应用中则经常使用 AFTER触发器,因为元组修改成功后才有必要运行触发器,而成功地完成修改意味着成功地通过了该表的引用完整性约束。
SQL> SET SCHEMA OTHER;
操作已执行
已用时间: 0.285(毫秒). 执行号:0.
SQL> SELECT * FROM OTHER.READER;
行号 READER_ID NAME AGE GENDER MAJOR
---------- ----------- ----- ----------- ------ --------
1 10 Bill 19 M Computer
2 11 Susan 18 F History
3 12 John 19 M Computer
已用时间: 0.550(毫秒). 执行号:2437.
SQL> CREATE OR REPLACE TRIGGER TRG_INS_BEFORE
BEFORE INSERT ON OTHER.READER
FOR EACH ROW
BEGIN
:NEW.READER_ID:=:NEW.READER_ID+1;
END
/
2 3 4 5 6 7 操作已执行
已用时间: 37.898(毫秒). 执行号:2438.
SQL> INSERT INTO OTHER.READER(READER_ID, NAME, AGE, GENDER, MAJOR) VALUES(13, 'TEST', 20, 'F', 'HISTORY');
影响行数 1
已用时间: 0.842(毫秒). 执行号:2439.
SQL> COMMIT;
操作已执行
已用时间: 0.923(毫秒). 执行号:2440.
SQL> SELECT * FROM OTHER.READER;
行号 READER_ID NAME AGE GENDER MAJOR
---------- ----------- ----- ----------- ------ --------
1 10 Bill 19 M Computer
2 11 Susan 18 F History
3 12 John 19 M Computer
4 14 TEST 20 F HISTORY
已用时间: 1.372(毫秒). 执行号:2441.
SQL> SET SCHEMA SYSDBA;
此为BEFORE触发器展示。
SQL> SET SCHEMA OTHER;
操作已执行
已用时间: 0.260(毫秒). 执行号:0.
SQL> CREATE TABLE T_TEMP(C1 INT,C2 CHAR(20));
操作已执行
已用时间: 6.882(毫秒). 执行号:2444.
SQL> CREATE OR REPLACE TRIGGER TRG_INS_AFTER
AFTER INSERT ON OTHER.READER
FOR EACH ROW
BEGIN
INSERT INTO T_TEMP VALUES(:NEW.READER_ID, 'INSERT ON READER');
END
/
2 3 4 5 6 7 操作已执行
已用时间: 12.320(毫秒). 执行号:2445.
SQL> INSERT INTO OTHER.READER(READER_ID, NAME, AGE, GENDER, MAJOR) VALUES(15, 'TEST1', 22, 'F', 'COMPUTER');
影响行数 1
已用时间: 0.952(毫秒). 执行号:2446.
SQL> COMMIT;
操作已执行
已用时间: 3.662(毫秒). 执行号:2447.
SQL> SELECT * FROM OTHER.READER;
行号 READER_ID NAME AGE GENDER MAJOR
---------- ----------- ----- ----------- ------ --------
1 10 Bill 19 M Computer
2 11 Susan 18 F History
3 12 John 19 M Computer
4 14 TEST 20 F HISTORY
5 16 TEST1 22 F COMPUTER
已用时间: 0.620(毫秒). 执行号:2448.
SQL> SELECT * FROM T_TEMP;
行号 C1 C2
---------- ----------- --------------------
1 16 INSERT ON READER
已用时间: 0.606(毫秒). 执行号:2449.
此为AFTER触发器展示
SQL> CREATE TABLE T1(A INT,B INT);
操作已执行
已用时间: 8.412(毫秒). 执行号:2450.
SQL> INSERT INTO T1 VALUES(10,10);
INSERT INTO T1 VALUES(11,11);
影响行数 1
已用时间: 0.557(毫秒). 执行号:2451.
SQL> 影响行数 1
已用时间: 0.314(毫秒). 执行号:2452.
SQL> COMMIT;
操作已执行
已用时间: 0.921(毫秒). 执行号:2453.
SQL> CREATE VIEW V1 AS SELECT * FROM T1;
操作已执行
已用时间: 12.613(毫秒). 执行号:2454.
SQL> CREATE OR REPLACE TRIGGER TRI1
INSTEAD OF UPDATE ON V1
BEGIN
INSERT INTO T1 VALUES(111,111);
END
/
2 3 4 5 6 操作已执行
已用时间: 18.962(毫秒). 执行号:2455.
SQL> UPDATE V1 SET A=100 WHERE A=10;
影响行数 1
已用时间: 1.261(毫秒). 执行号:2456.
SQL> COMMIT;
操作已执行
已用时间: 4.811(毫秒). 执行号:2457.
SQL> SELECT * FROM V1;
行号 A B
---------- ----------- -----------
1 10 10
2 11 11
3 111 111
已用时间: 1.223(毫秒). 执行号:2458.
SQL> SELECT * FROM T1;
行号 A B
---------- ----------- -----------
1 10 10
2 11 11
3 111 111
已用时间: 0.626(毫秒). 执行号:2459.
SQL> SET SCHEMA SYSDBA;
INSTEAD OF 触发器展示,由上面的查询结果可以看出。更新操作并没有成功,而是被触发器中的替换动作替换了。
触发器谓词的使用
SQL> SET SCHEMA OTHER;
操作已执行
已用时间: 0.288(毫秒). 执行号:0.
SQL> CREATE OR REPLACE TRIGGER LogChanges
AFTER INSERT OR DELETE OR UPDATE ON OTHER.READER
FOR EACH ROW
DECLARE
v_ChangeType CHAR(1);
BEGIN
IF INSERTING THEN
v_ChangeType := 'I';
ELSIF UPDATING THEN
v_ChangeType := 'U';
ELSE
v_ChangeType := 'D';
END IF;
INSERT INTO OTHER.READERAUDIT
VALUES
(v_ChangeType, USER, SYSDATE,
:OLD.READER_ID, :OLD.NAME, :OLD.AGE, :OLD.GENDER, :OLD.MAJOR,
:NEW.READER_ID, :NEW.NAME, :NEW.AGE, :NEW.GENDER, :NEW.MAJOR);
END
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 操作已执行
已用时间: 22.807(毫秒). 执行号:2463.
SQL> SELECT * FROM OTHER.READERAUDIT;
未选定行
已用时间: 1.392(毫秒). 执行号:2464.
SQL> INSERT INTO OTHER.READER(READER_ID, NAME, AGE, GENDER, MAJOR) VALUES(17, 'TEST2', 20, 'F', 'COMPUTER');
COMMIT;
影响行数 1
已用时间: 1.311(毫秒). 执行号:2465.
SQL> 操作已执行
已用时间: 0.818(毫秒). 执行号:2466.
SQL> SELECT * FROM OTHER.READERAUDIT;
行号 CHANGE_TYPE CHANGED_BY OP_TIMESTAMP OLD_READER_ID OLD_NAME OLD_AGE OLD_GENDER OLD_MAJOR NEW_READER_ID NEW_NAME NEW_AGE NEW_GENDER
---------- ----------- ---------- ------------ ------------- -------- ----------- ---------- --------- ------------- -------- ----------- ----------
NEW_MAJOR
---------
1 I SYSDBA 2023-09-02 NULL NULL NULL NULL NULL 18 TEST2 20 F
COMPUTER
已用时间: 0.296(毫秒). 执行号:2467.
SQL> SET SCHEMA SYSDBA;
操作已执行
已用时间: 0.286(毫秒). 执行号:0.
事件触发器
前面表级触发器都是基于表中数据的触发器,它通过针对相应表对象的插入/删除/修改等 DML 语句触发。DM 还支持事件触发器,包括库级和模式级触发器,这类触发器并不依赖于某个表,而是基于特定系统事件触发的,通过指定 DATABASE 或某个 SCHEMA 来表示事件触发器的作用区域。创建事件触发器的用户需要拥CREATE_TRIGGERCREATE_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 事件触发器。
与数据触发器不同,事件触发器不能影响对应触发事件的执行。它的主要作用是帮助管
理员监控系统运行发生的各类事件,进行一定程度的审计和监视工作。
SQL> CREATE TABLE T_EAF(
N INT,
SQLTEXT VARCHAR,
OBJECTNAME VARCHAR(128),
OBJECTTYPE VARCHAR(128),
OBJECTOWNER VARCHAR(128)
);
CREATE OR REPLACE TRIGGER TRIG_EAF_01 BEFORE DDL ON DATABASE
DECLARE
N NUMBER;
STR_STMT VARCHAR;
SQL_TEXT DM_NAME_LIST_T;
2 3 4 5 6 7 BEGIN
N := DM_SQL_TXT(SQL_TEXT); //N 为占用嵌套表单元个数
FOR I IN 1..N
LOOP
STR_STMT := STR_STMT || SQL_TEXT(I); //STR_STMT 为获取的 DDL 语句
END LOOP;
INSERT INTO T_EAF VALUES(N,STR_STMT,DM_DICT_OBJ_NAME, DM_DICT_OBJ_TYPE,
DM_DICT_OBJ_OWNER);
END;
/
操作已执行
已用时间: 9.945(毫秒). 执行号:2469.
SQL> 2 3 4 5 6 7 8 9 10 11 12 13 14 15 操作已执行
已用时间: 12.799(毫秒). 执行号:2470.
SQL> SELECT * FROM T_EAF;
未选定行
已用时间: 0.765(毫秒). 执行号:2471.
SQL> CREATE SCHEMA SYSTEST;
2 /
操作已执行
已用时间: 9.811(毫秒). 执行号:2472.
SQL> CREATE TABLE T_SYSTEST(C1 INT)
2 /
操作已执行
已用时间: 13.140(毫秒). 执行号:2473.
SQL> SELECT * FROM T_EAF;
行号 N SQLTEXT OBJECTNAME OBJECTTYPE OBJECTOWNER
---------- ----------- ------------------------------ ---------- ---------- -----------
1 1 CREATE SCHEMA SYSTEST; SYSTEST SCHEMA SYSTEST
2 1 CREATE TABLE T_SYSTEST(C1 INT) T_SYSTEST TABLE SYSDBA
已用时间: 0.641(毫秒). 执行号:2474.
事件触发器展示,可以看出两个DDL语句皆被记录。
时间触发器
时间触发器属于一种特殊的事件触发器,它使得用户可以定义一些有规律性执行的、定
点执行的任务,比如在晚上服务器负荷轻的时候通过时间触发器做一些更新统计信息的操
作、自动备份操作等等,因此时间触发器是非常有用的。
SQL> CREATE OR REPLACE TRIGGER timer2
AFTER TIMER on database
for each 1 day for each 1 minute
BEGIN
print '事件触发器展示';
END
/2 3 4 5 6 7
操作已执行
已用时间: 16.668(毫秒). 执行号:2475.
关闭与打开触发器
SQL> ALTER TRIGGER TRIG_EAF_01 DISABLE;
操作已执行
已用时间: 8.993(毫秒). 执行号:2476.
SQL> ALTER TRIGGER TRIG_EAF_01 ENABLE;
操作已执行
已用时间: 8.237(毫秒). 执行号:2477.
SQL> ALTER TRIGGER TRIG_EAF_01 COMPILE;
操作已执行
已用时间: 12.609(毫秒). 执行号:2479.
触发器重编
删除触发器
SQL> SELECT * FROM USER_TRIGGERS;
行号 TRIGGER_NAME TRIGGERING_TYPE TRIGGERING_EVENT TABLE_OWNER BASE_OBJECT_TYPE TABLE_NAME COLUMN_NAME STATUS
---------- ------------ --------------- ---------------- ----------- ---------------- ---------- ----------- ------
TRIGGER_BODY
-------------------------------------------------------------------------------------------------------------------------------------------------
1 DEL_TRG BEFORE STMT DELETE PERSON TABLE PERSON NULL Y
CREATE OR REPLACE TRIGGER DEL_TRG
BEFORE DELETE
ON PERSON
BEGIN
PRINT '您正在对表 PERSON 进行删除操作';
END;
2 TRG_DEL_ROW BEFORE ROW DELETE PERSON TABLE PERSON NULL Y
CREATE OR REPLACE TRIGGER TRG_DEL_ROW
BEFORE DELETE ON PERSON.PERSON
FOR EACH ROW
BEGIN
PRINT 'DELETE' || :OLD.NAME|| ' ON PERSON';
END
3 TRG_UPD AFTER STMT UPDATE PERSON TABLE PERSON NULL Y
CREATE OR REPLACE TRIGGER TRG_UPD
AFTER UPDATE OF NAME,PHONE ON PERSON.PERSON
BEGIN
PRINT 'UPDATE OPERATION ON COLUMNS NAME OR PHONE OF PERSON';
END;
已用时间: 0.699(毫秒). 执行号:2244.
SQL> DROP TRIGGER TRG_UPD;
DROP TRIGGER TRG_UPD;
第1 行附近出现错误[-2110]:无效的触发器名[TRG_UPD].
已用时间: 0.221(毫秒). 执行号:0.
删除其他模式下的触发器时,需要加模式名。
SQL> DROP TRIGGER PERSON.TRG_UPD;
操作已执行
已用时间: 14.650(毫秒). 执行号:2245.
SQL> DROP TRIGGER PERSON.DEL_TRG;
操作已执行
已用时间: 14.152(毫秒). 执行号:2246.
SQL> DROP TRIGGER PERSON.TRG_DEL_ROW;
操作已执行
已用时间: 15.108(毫秒). 执行号:2247.
3.总结
触发器常用于自动完成一些数据库的维护工作。例如,触发器可以具有以下功能:
1. 可以对表自动进行复杂的安全性、完整性检查;
2. 可以在对表进行 DML 操作之前或者之后进行其它处理;
3. 进行审计,可以对表上的操作进行跟踪;
4. 实现不同节点间数据库的同步更新。
触发器是在相关的事件发生时由服务器自动隐式地激发。触发器是激发它们的语句的一个组成部分,即直到一个语句激发的所有触发器执行完成之后该语句才结束,而其中任何一个触发器执行的失败都将导致该语句的失败,触发器所做的任何工作都属于激发该触发器的语句。
https://eco.dameng.com