如何解决Oracle After Delete Trigger…如何避免突变表ORA-04091?
| 假设我们具有以下表结构:documents docmentStatusHistory status
+---------+ +--------------------+ +----------+
| docId | | docStatusHistoryId | | statusId |
+---------+ +--------------------+ +----------+
| ... | | docId | | ... |
+---------+ | statusId | +----------+
| ... |
+--------------------+
显而易见,但值得一提的是,文档的当前状态是最后输入的状态历史记录。
系统运行缓慢但肯定会降低性能,我建议将上述结构更改为:
documents docmentStatusHistory status
+--------------+ +--------------------+ +----------+
| docId | | docStatusHistoryId | | statusId |
+--------------+ +--------------------+ +----------+
| currStatusId | | docId | | ... |
| ... | | statusId | +----------+
+--------------+ | ... |
+--------------------+
这样,我们就可以将文档的当前状态保存在应有的位置。
由于遗留应用程序的构建方式,我无法更改遗留应用程序上的代码以更新文档表上的当前状态。
在这种情况下,我必须为规则打开一个例外,以不惜一切代价避免触发器,这仅仅是因为我无法访问旧版应用程序代码。
我创建了一个触发器,该触发器每次将新状态添加到状态历史记录时都会更新文档的当前状态,它的工作原理就像一个超级按钮。
但是,在一种晦涩且很少使用的情况下,需要删除最后一个状态历史记录,而不是简单地添加一个新的状态历史记录。因此,我创建了以下触发器:
create or replace trigger trgD_History
after delete on documentStatusHistory
for each row
currentStatusId number;
begin
select statusId
into currentStatusId
from documentStatusHistory
where docStatusHistoryId = (select max(docStatusHistoryId)
from documentStatusHistory
where docId = :old.docId);
update documentos
set currStatusId = currentStatusId
where docId = :old.docId;
end;
那就是我得到臭名昭著的错误error3ѭ的地方。
我知道为什么会收到此错误,即使我将触发器配置为AFTER触发器也是如此。
问题是我看不到解决此错误的方法。我已经在网上搜索了一段时间,但到目前为止找不到任何有用的信息。
及时地,我们正在使用Oracle 9i。
解决方法
突变表错误的标准解决方法是创建
具有密钥集合的软件包(在这种情况下为docId \)。临时表也可以
初始化集合的before语句触发器
一个行级触发器,使用每个已更改的docId填充集合
after语句触发器,它遍历集合并执行实际的UPDATE
所以像
CREATE OR REPLACE PACKAGE pkg_document_status
AS
TYPE typ_changed_docids IS TABLE OF documentos.docId%type;
changed_docids typ_changed_docids := new typ_changed_docids ();
<<other methods>>
END;
CREATE OR REPLACE TRIGGER trg_init_collection
BEFORE DELETE ON documentStatusHistory
BEGIN
pkg_document_status.changed_docids.delete();
END;
CREATE OR REPLACE TRIGGER trg_populate_collection
BEFORE DELETE ON documentStatusHistory
FOR EACH ROW
BEGIN
pkg_document_status.changed_docids.extend();
pkg_document_status.changed_docids( pkg_document_status.changed_docids.count() ) := :old.docId;
END;
CREATE OR REPLACE TRIGGER trg_use_collection
AFTER DELETE ON documentStatusHistory
BEGIN
FOR i IN 1 .. pkg_document_status.changed_docids.count()
LOOP
<<fix the current status for pkg_document_status.changed_docids(i) >>
END LOOP;
pkg_document_status.changed_docids.delete();
END;
,似乎是这个问题的重复
看看汤姆·凯特(Tom Kyte)对此的看法
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。