触发器在被同一个表触发后无法读取该表

时间:2015-12-18 19:25:46

标签: oracle plsql oracle11g oracle-sqldeveloper

假设我有一张表格如下 -

create table employees 
(
 eno      number(4) not null primary key, 
 ename    varchar2(30),
 zip      number(5) references zipcodes,
 hdate    date
);

我使用以下代码块

创建了一个触发器
create or replace TRIGGER COPY_LAST_ONO
AFTER INSERT ON ORDERS
FOR EACH ROW
DECLARE

ID_FROM_ORDER_TABLE VARCHAR2(10);

BEGIN

SELECT MAX(ORDERS.ONO)INTO ID_FROM_ORDER_TABLE from ORDERS ;
DBMS_OUTPUT.PUT_LINE(ID_FROM_ORDER_TABLE);

INSERT INTO BACKUP_ONO VALUES( VALUE1, VALUE2,VALUE3, ID_FROM_ORDER_TABLE);

END;

触发器在插入后触发并尝试从触发它的表中读取(逻辑上是duhh!)但oracle正在给我一个错误并要求我修改触发器以便它不会读取表。错误代码 -

Error report -
SQL Error: ORA-04091: table TEST1.ORDERS is mutating, trigger/function may not see it
ORA-06512: at "TEST1.COPY_LAST_ONO", line 8
ORA-04088: error during execution of trigger 'TEST1.LOG_INSERT'
04091. 00000 -  "table %s.%s is mutating, trigger/function may not see it"
*Cause:    A trigger (or a user defined plsql function that is referenced in
       this statement) attempted to look at (or modify) a table that was
       in the middle of being modified by the statement which fired it.
*Action:   Rewrite the trigger (or function) so it does not read that table.

我试图通过此触发器实现的目的是将最后的INSERTED ONO(这是ORDER表的主键)立即复制到另一个表中INSERTED。我没有得到的是,为什么oracle抱怨?触发器正在尝试读取插入AFTER

想法?溶液

非常感谢

3 个答案:

答案 0 :(得分:4)

如果您尝试记录刚刚插入的ONO,请使用:new.ono并完全跳过选择:

INSERT INTO BACKUP_ONO VALUES( VALUE1, VALUE2,VALUE3, :new.ono);

我不相信你可以从你正在插入的表中选择,因为尚未发布提交,因此变异表错误。

P.S。考虑不要缩写。明确下一个开发人员,并将其称为ORDER_NUMBER或至少一个普遍接受的缩写,如ORDER_NBR,无论贵公司的命名标准是什么。 : - )

仅供参考 - 如果您要更新,您也可以访问:OLD.column,更新前的值(当然,如果该列不是主键列)。

答案 1 :(得分:2)

扩大@ Gary_W的答案:

Oracle不允许行触发器(其中包含FOR EACH ROW)触发以任何方式定义触发器的表 - 您不能发出SELECT,INSERT,UPDATE或者从触发器或它调用的任何东西中删除该表(所以,不,你不能通过调用一个为你做脏工作的存储过程来避开这个 - 但是好好思考!:-)。我的理解是这样做是为了防止你可能称之为&#34;触发循环&#34; - 即,满足触发条件并执行触发器的PL / SQL块;该块然后做一些导致触发器再次被触发的东西;调用触发器的PL / SQL块;触发器的代码修改了另一行;无限的,无限的。一般来说,这应该被视为警告您的逻辑要么是非常丑陋,要么是您在错误的地方实施它。 (See here for info on the evil of business logic in triggers)。如果您发现确实 认真需要这样做(我已经与Oracle及其他数据库合作多年 - 我 真的< / em>必须做一次 - 并且可能Cthulhu怜悯我的灵魂:-) you can use a compound trigger这可以让你解决这些问题 - 但是说真的,如果你在这样的洞里面是最好的选项是重新处理数据,因此您不必这样做。

祝你好运。

答案 2 :(得分:0)

修改您的触发器以使用 PRAGMA AUTONOMOUS_TRANSACTION

create or replace TRIGGER COPY_LAST_ONO
AFTER INSERT ON ORDERS
FOR EACH ROW
DECLARE

    ID_FROM_ORDER_TABLE VARCHAR2(10);
    PRAGMA AUTONOMOUS_TRANSACTION; -- Modification
BEGIN
.
.
.