对于复杂的视图,无法直接对视图进行修改。或者在某些情况下,需要将对视图的修改转化为另外一种操作,这种情况下可以使用INSTEAD OF TRIGGER。
看一个简单的例子,下面建立一个UNION ALL视图,其中T1表是不能修改的,对视图T所有的修改都重定位到T2上。
SQL> CREATE TABLE T1 (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30), OTHERS VARCHAR2(30));
表已创建。
SQL> CREATE TABLE T2 (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30), OTHERS VARCHAR2(30));
表已创建。
SQL> CREATE VIEW T AS SELECT * FROM T1 UNION ALL SELECT * FROM T2;
视图已创建。
SQL> INSERT INTO T1 VALUES (1, 'T1', 'TEST');
已创建 1 行。
SQL> INSERT INTO T2 VALUES (2, 'T2', 'TEST T2');
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> SELECT * FROM T;
ID NAME OTHERS
---------- ------------------------------ ------------------------------
1 T1 TEST
2 T2 TEST T2
SQL> INSERT INTO T VALUES (3, 'T', 'TEST AGAIN');
INSERT INTO T VALUES (3, 'T', 'TEST AGAIN')
*第 1 行出现错误:
ORA-01732: 此视图的数据操纵操作非法
SQL> DELETE T;
DELETE T
*第 1 行出现错误:
ORA-01732: 此视图的数据操纵操作非法
SQL> UPDATE T SET OTHERS = 'UPDATED' WHERE ID = 2;
UPDATE T SET OTHERS = 'UPDATED' WHERE ID = 2
*第 1 行出现错误:
ORA-01732: 此视图的数据操纵操作非法
下面建立INSTEAD OF触发器:
SQL> CREATE OR REPLACE TRIGGER INSTEADOF_T
2 INSTEAD OF INSERT OR UPDATE OR DELETE ON T
3 REFERENCES OLD AS OLD NEW AS NEW
4 FOR EACH ROW
5 BEGIN
6 IF INSERTING THEN
7 INSERT INTO T2 VALUES (:NEW.ID, :NEW.NAME, :NEW.OTHERS);
8 ELSIF UPDATING THEN
9 UPDATE T2 SET ID = :NEW.ID, NAME = :NEW.NAME, OTHERS = :NEW.OTHERS
10 WHERE ID = :OLD.ID;
11 ELSIF DELETING THEN
12 DELETE T2 WHERE ID = :OLD.ID;
13 END IF;
14 END;
15 /
触发器已创建
SQL> INSERT INTO T VALUES (3, 'T', 'TEST AGAIN');
已创建 1 行。
SQL> SELECT * FROM T;
ID NAME OTHERS
---------- ------------------------------ ------------------------------
1 T1 TEST
2 T2 TEST T2
3 T TEST AGAIN
SQL> SELECT * FROM T1;
ID NAME OTHERS
---------- ------------------------------ ------------------------------
1 T1 TEST
SQL> SELECT * FROM T2;
ID NAME OTHERS
---------- ------------------------------ ------------------------------
2 T2 TEST T2
3 T TEST AGAIN
SQL> UPDATE T SET OTHERS = 'UPDATED';
已更新3行。
SQL> SELECT * FROM T;
ID NAME OTHERS
---------- ------------------------------ ------------------------------
1 T1 TEST
2 T2 UPDATED
3 T UPDATED
SQL> DELETE T;
已删除3行。
SQL> SELECT * FROM T;
ID NAME OTHERS
---------- ------------------------------ ------------------------------
1 T1 TEST
SQL> SELECT * FROM T2;
未选定行
INSTEAD OF触发器已经发挥了作用,将所有对视图T的修改都重定向到T2上,不过UPDATE的修改采用了偷懒的写法,这里并没有对修改的列进行检测,而是采用了全部更新的方法。这样可以减少代码量,但是会对一些没有发生修改的字段进行更新,导致REDO和UNDO的增加。如果T2表中还存在基于字段更新的触发器的话,就不能使用这种办法了。