仅在 old = new 上进行选择时获取变异表 (ORA-04091)
Posted
技术标签:
【中文标题】仅在 old = new 上进行选择时获取变异表 (ORA-04091)【英文标题】:Getting Mutating Tables (ORA-04091) when just doing a select on old = new 【发布时间】:2013-12-20 09:08:40 【问题描述】:我在 oracle 中对触发器进行简单的选择计数时遇到问题。我不断收到 ORA-04091:
AFTER INSERT OR UPDATE ON table
FOR EACH ROW
DECLARE
legal_amount INT;
BEGIN
SELECT count(*)
INTO legal_amount
FROM table
WHERE :old.TBL_ID = :new.TBL_ID; /* Many more AND clauses here */
IF (legal_amount = 0) THEN
no_legal_amount_procedure(:new.TBL_ID);
END IF;
ELSE
legal_amount_procedure(:new.TBL_ID);
END IF;
END;
是导致 ORA-04091 错误的 select 语句。
我们之前解决此问题的方法是执行 PRAGMA AUTONOMOUS_TRANSACTION,但我需要摆脱它,因为它会导致其他问题(在自治事务中选择与旧事务所需的未提交数据 = null 结果)。
我也尝试按照发现 here 的 asktom 教程进行操作,但由于 AND 子句的数量以及这是 FOR EACH ROW 语句的事实而没有成功。我希望这是最后的手段。
我知道在触发器中执行业务逻辑是不好的。但是如果无法访问源代码,这是解决问题的唯一方法。
【问题讨论】:
程序no_legal_amount_procedure
和legal_amount_procedure
是干什么用的? SELECT count(*)..
是否也触及被修改的行?在AFTER INSERT
触发器中使用:old
和:new
。不应该有一些条件,比如'IF UPDATING THEN ... IF INSERTING THEN ...'。
这只是一个简化的例子,可能不是 100% 正确的语法。是的,有一个 IF UPDATING THEN 语句。这两个legal_amount 过程实际上是一个关于如何转换文档的两个不同标准的过程。
这些程序有可能修改它的参数吗?它们是定义为IN
IN OUT
还是IN OUT NOCOPY
?。
这似乎没有意义:WHERE :old.TBL_ID = :new.TBL_ID
,因为它本质上意味着“如果我没有更新 TBL_ID”。你的意思是WHERE TBL_ID = :new.TBL_ID
?
如果您必须在触发器中查询同一张表,请尝试复合触发器。但是查询同一张表不是个好主意……
【参考方案1】:
用存储过程包装整个插入,而不是触发器。这是一个仅用于 INSERT 的示例:
CREATE OR REPLACE PROCEDURE insert_table (in_tbl_id INTEGER, ...[other attrs])
IS
v_legal_amount INTEGER;
BEGIN
SELECT count(*)
INTO v_legal_amount
FROM table
WHERE tbl_id = in_tbl_id
/* Many more AND clauses here */;
INSERT INTO table VALUES (tbl_id, ...);
IF (v_legal_amount = 0) THEN
no_legal_amount_procedure (in_tbl_id);
ELSE
legal_amount_procedure (in_tbl_id);
END IF;
END;
【讨论】:
以上是关于仅在 old = new 上进行选择时获取变异表 (ORA-04091)的主要内容,如果未能解决你的问题,请参考以下文章