日期:2014-05-16  浏览次数:20484 次

用视图进行多表更新,使用INSTEAD OF 触发器
原文链接:http://book.51cto.com/art/201007/212376.htm

使用INSTEAD OF 触发器

如果创建一个对象视图,则可以使用INSTEAD OF 触发器来告诉Oracle 如何更新作为视图一部分的基表。可以在对象视图或标准关系视图中使用INSTEAD OF 触发器。

例如,如果一个视图涉及两个表的连接,那么用户在视图中更新记录的能力有限。但是,如果使用INSTEAD OF 触发器,那么当用户试图通过该视图更改值时,可以告诉Oracle 如何在表中更新、删除或插入记录。INSTEAD OF 触发器中的程序代码代替了输入的update、delete 或insert 命令。

例如,有一个连接BOODSHELF 表与BOOKSHELF_AUTHOR 表的视图:

create or replace view AUTHOR_PUBLISHER as  select BA.AuthorName, Title, B.Publisher  from BOOKSHELF_AUTHOR BA inner join BOOKSHELF B  using (Title);
可以从该视图中选择相应的值—— 而且如果使用INSTEAD OF 触发器,就可以通过该视图执行数据操作。考虑以下记录:

select AuthorName, Publisher from AUTHOR_PUBLISHER  where AuthorName = 'W. P. KINSELLA'; 
AUTHORNAME PUBLISHER  
--------------------------------------------- ----------  
W. P. KINSELLA MARINER  
W. P. KINSELLA
BALLANTINE 如果试图更新Publisher 的值,则更新会失败:

update AUTHOR_PUBLISHER  set Publisher = 'MARINER' where AuthorName = 'W. P. KINSELLA';  set Publisher = 'MARINER' *  ERROR at line 2:  ORA-01779: cannot modify a column which maps to a  non key-preserved table
问题是Oracle 不能确定BOOKSHELF 表中哪些记录中的哪些出版商要更新。为了通过该视图执行更新,需要使用INSTEAD OF 触发器。

在下面的程序清单中,创建了AUTHOR_PUBLISHER 视图的一个INSTEAD OF 触发器:

create or replace trigger AUTHOR_PUBLISHER_UPDATE  instead of UPDATE on AUTHOR_PUBLISHER  for each row  begin  if :old.Publisher <> :new.Publisher  then  update BOOKSHELF  set Publisher = :new.Publisher  where Title = :old.Title;  end if;  if :old.AuthorName <> :new.AuthorName  then  update BOOKSHELF_AUTHOR  set AuthorName = :new.AuthorName  where Title = :old.Title;  end if;  end; 
该触发器的第一部分命名触发器,并通过instead of 子句描述其用途。顾名思义该触发器的功能很明显:用于支持对AUTHOR_PUBLISHER 视图执行的update 命令。它是一个行级触发器;可以处理每个变化的行,如下所示:

create trigger AUTHOR_PUBLISHER_UPDATE  instead of UPDATE on AUTHOR_PUBLISHER  for each row 该触发器的下一部分告诉Oracle 如何处理update 操作。首先要检查的是触发器体内部的Publisher 值。如果旧的Publisher 值等于新的Publisher 值,则不做任何修改。如果两个值不相同,则BOOKSHELF 表被更新为新的Publisher 值:

begin  if :old.Publisher <> :new.Publisher  then  update BOOKSHELF  set Publisher = :new.Publisher  where Title = :old.Title;  end if;
触发器的下一部分判断AuthorName 的值是否有变化。如果AuthorName 的值被修改,则更新BOOKSHELF 表,以反映新的AuthorName 值:

if :old.AuthorName <> :new.AuthorName  then  update BOOKSHELF_AUTHOR  set AuthorName = :new.AuthorName  where Title = :old.Title;  end if;
因此,该视图依赖两个表—— BOOKSHELF 和BOOKSHELF_AUTHOR—— 并且该视图的update 操作可以更新一个表也可以同时更新两个表。为了支持对象视图而引入的INSTEADOF 触发器是应用程序开发的有力工具。

现在可以直接更新AUTHOR_PUBLISHER 视图并使触发器恰当地更新基表。例如,以下命令将更新BOOKSHELF 表:

update AUTHOR_PUBLISHER  set Publisher = 'MARINER' where AuthorName = 'W. P. KINSELLA';  2 rows updated. 

可以通过查询BOOKSHELF 表,来验证update 操作:

select Publisher from BOOKSHELF  where Title in  (select Title from BOOKSHELF_AUTHOR  where AuthorName = 'W. P. KINSELLA');  
PUBLISHER  
--------------------  
MARINER  
MARINER 

INSTEAD OF 触发器的功能十分强大。如本示例所示,使用PL/SQL 内可用的流控制逻辑,可以使用该触发器在不同的数据库表上进行操作。在处理对象视图时,可以使用INSTEADOF 触发器将对象视图的DML 重定位到这些视图的基表上。