11g中对于触发器部分有了一定的增强,主要表现在两个方面。一个是对触发器的触发顺序可以进行控制。另一个是可以定义一个复合触发器。
上一篇介绍了触发器的触发顺序,这一篇来介绍一下复合触发器。复合触发器中可以包括BEFORE STATEMENT、BEFORE EACH ROW、AFTER EACH ROW和AFTER STATEMENT四个部分,将四种类型的触发器集成在一个触发器中,如果需要多个类型的触发器配合使用,采用复合触发器会显得逻辑更加清晰,而且不容易出现错误。在复合触发器中定义的变量可以在不同类型的触发语句中使用,不再需要使用外部包
存储中间结果。而且利用复合触发器的批量操作还可以提高触发器的性能。
下面先看一个简单的COMPOUND TRIGGER的语法:
SQL> CREATE TABLE T (ID NUMBER, NAME VARCHAR2(30));
表已创建。
SQL> CREATE OR REPLACE TRIGGER TRI_COMPOUND FOR INSERT OR UPDATE OR DELETE ON T
2 COMPOUND TRIGGER
3 BEFORE STATEMENT IS
4 BEGIN
5 DBMS_OUTPUT.PUT_LINE('BEFORE STATEMENT');
6 END BEFORE STATEMENT;
7
8 BEFORE EACH ROW IS
9 BEGIN
10 DBMS_OUTPUT.PUT_LINE('BEFORE EACH ROW');
11 END BEFORE EACH ROW;
12
13 AFTER EACH ROW IS
14 BEGIN
15 DBMS_OUTPUT.PUT_LINE('AFTER EACH ROW');
16 END AFTER EACH ROW;
17
18 AFTER STATEMENT IS
19 BEGIN
20 DBMS_OUTPUT.PUT_LINE('AFTER STATEMENT');
21 END AFTER STATEMENT;
22 END;
23 /
触发器已创建
SQL> SET SERVEROUT ON
SQL> INSERT INTO T SELECT ROWNUM, TNAME FROM TAB;
BEFORE STATEMENT
BEFORE EACH ROW
AFTER EACH ROW
BEFORE EACH ROW
AFTER EACH ROW
BEFORE EACH ROW
AFTER EACH ROW
AFTER STATEMENT
已创建3行。
了解了COMPOUND触发器的语法,下面看看如何利用COMPOUND TRIGGER来简化变异表的处理。在以前的一篇文章中,介绍了:通过触发器复制包含LONG类型的表:
http://yangtingkun.itpub.net/post/468/41936里面包括了变异表触发器的处理方法,下面用COMPOUND TRIGGER来解决这个
问题:
SQL> CREATE TABLE T_LONG (ID NUMBER PRIMARY KEY, COMMENTS LONG);
表已创建。
SQL> CREATE TABLE T_LONG_LOG (ID NUMBER PRIMARY KEY, COMMENTS CLOB);
表已创建。
SQL> CREATE OR REPLACE TRIGGER TRI_T_LONG_COMPOUND FOR INSERT ON T_LONG
2 COMPOUND TRIGGER
3 TYPE T_NUMBER IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
4 V_ID T_NUMBER;
5 BEFORE EACH ROW IS
6 BEGIN
7 V_ID(V_ID.COUNT + 1) := :NEW.ID;
8 END BEFORE EACH ROW;
9
10 AFTER STATEMENT IS
11 BEGIN
12 FORALL I IN 1..V_ID.COUNT
13 INSERT INTO T_LONG_LOG SELECT ID, TO_LOB(COMMENTS) FROM T_LONG WHERE ID = V_ID(I);
14 END AFTER STATEMENT;
15 END;
16 /
触发器已创建
SQL> INSERT INTO T_LONG SELECT ROWNUM, TNAME FROM TAB;
已创建5行。
SQL> COL COMMENTS FORMAT A40
SQL> SELECT * FROM T_LONG;
ID COMMENTS
---------- ----------------------------------------
1 T
2 T_LONG
3 T_LONG_LOG
4 T_SESSION
5 T_SESSION_STAT
SQL> SELECT * FROM T_LONG_LOG;
ID COMMENTS
---------- ----------------------------------------
1 T
2 T_LONG
3 T_LONG_LOG
4 T_SESSION
5 T_SESSION_STAT
对比一下就可以看出,使用COMPOUND触发器要比建立三个触发器加一个包要简化很多,而且初始化,处理,清除等所有的步骤都在一起,也不容易出错。
而且由于COMPOUND所有的代码可以集中在一起,现在很多操作可以批量处理,这样COMPOUND还可以提高性能。
现在仍然使用第一个例子,为T增加一张LOG表,对T表所有的INSERT都同时插入到LOG表中,对比一下COMPOUND TRIGGER和普通TRIGGER的性能差异:
SQL> CREATE TABLE T_LOG (ID NUMBER, NAME VARCHAR2(30));
表已创建。
SQL> TRUNCATE TABLE T;
表被截断。
下面建立两种不同的触发器,二者的功能一致,都是向T_LOG表中插入T表新插入的
数据:
SQL> CREATE OR REPLACE TRIGGER TRI_COMPOUND FOR INSERT ON T DISABLE
2 COMPOUND TRIGGER
3 TYPE T_NUMBER IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
4 TYPE T_VARCHAR2 IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
5 V_ID T_NUMBER;
6 V_NAME T_VARCHAR2;
7 AFTER EACH ROW IS
8 BEGIN
9 V_ID(V_ID.COUNT + 1) := :NEW.ID;
10 V_NAME(V_NAME.COUNT + 1) := :NEW.NAME;
11 END AFTER EACH ROW;
12
13 AFTER STATEMENT IS
14 BEGIN
15 FORALL I IN 1..V_ID.COUNT
16 INSERT INTO T_LOG VALUES (V_ID(I), V_NAME(I));
17 END AFTER STATEMENT;
18 END;
19 /
触发器已创建
SQL> CREATE OR REPLACE TRIGGER TRI_A_EACHROW AFTER INSERT ON T
2 FOR EACH ROW DISABLE
3 BEGIN
4 INSERT INTO T_LOG VALUES (:NEW.ID, :NEW.NAME);
5 END;
6 /
触发器已创建
两个触发器都处于DISABLE状态,向T表插入数据,然后依次ENABLE其中的一个触发器,重复插入操作,对比三次的性能:
SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;
已创建68345行。
SQL> TRUNCATE TABLE T;
表被截断。
SQL> SET TIMING ON
SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;
已创建68345行。
已用时间: 00: 00: 00.75
SQL> TRUNCATE TABLE T;
表被截断。
SQL> ALTER TRIGGER TRI_COMPOUND ENABLE;
触发器已更改
SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;
已创建68345行。
已用时间: 00: 00: 05.59
SQL> TRUNCATE TABLE T;
表被截断。
SQL> TRUNCATE TABLE T_LOG;
表被截断。
SQL> ALTER TRIGGER TRI_COMPOUND DISABLE;
触发器已更改
SQL> ALTER TRIGGER TRI_A_EACHROW ENABLE;
触发器已更改
SQL> INSERT INTO T SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;
已创建68345行。
已用时间: 00: 00: 17.31
第一次不记录时间,为了避免CACHE的影响,后面三次记录时间,分别对应不启用触发器、启用COMPOUND触发器和启动AFTER EACH ROW触发器三种情况。对比三次的执行时间,可以看到使用了COMPOUND的FORALL批量处理功能,获得的性能提高还是非常明显的。