假设我们有以下表格结构:
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;
那就是我得到臭名昭着的错误ORA-04091.
我明白为什么我收到此错误,即使我将触发器配置为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;