ORA-04091(变异表)只有在填写了特定字段的情况下

时间:2019-07-10 09:44:59

标签: oracle oracle11g database-trigger

得到一个表,该表本身具有一个键:

+----+-----------+-----------+
+ ID + ID_PARENT + IS_PARENT +
+----+-----------+-----------+
+  1 +    (null) +         0 +
+  2 +    (null) +         1 +
+  3 +         2 +         0 +
+----+-----------+-----------+

如您所见,ID 1是独立的,32的子代。

现在我想要一个触发器,在INSERT / UPDATE上...

  • 错误,如果插入的行是它自己的父行(不可能)
  • 如果它有一个ID_PARENT,请将父级的IS_PARENT设置为1

这是我的方法:

CREATE OR REPLACE TRIGGER tri_table_set_parent
    BEFORE
    INSERT OR UPDATE ON table
    FOR EACH ROW
    WHEN ( new.id_parent IS NOT NULL )
BEGIN
    IF :new.id = :new.id_parent
    THEN
        RAISE_APPLICATION_ERROR(-20666, 'A gap cant be the parent of itself. More information here: https://youtu.be/hqRZFWE1X_A');
    END IF;
    UPDATE table
    SET is_parent = 1
    WHERE id = :new.id_parent;
END;
/

该错误按预期工作,呜呼!但是现在插入时遇到了问题。

在没有 ID_PARENT的情况下插入行时,它可以工作(因为触发器根本不会触发)。

插入一行,其父级的ID_PARENT = (null)

INSERT INTO table (ID, ID_PARENT) VALUES (4, 1);

->有效!

但是插入一行,其父级得到一个ID_PARENT

INSERT INTO table (ID, ID_PARENT) VALUES (5, 3);

->错误:

ORA-04091: Tabelle TABLE wird gerade geändert, Trigger/Funktion sieht dies möglicherweise nicht
ORA-06512: in "TRI_TABLE_SET_PARENT", Zeile 6
ORA-04088: Fehler bei der Ausführung von Trigger "TRI_TABLE_SET_PARENT"
ORA-06512: in "TRI_TABLE_SET_PARENT", Zeile 6
ORA-04088: Fehler bei der Ausführung von Trigger "TRI_TABLE_SET_PARENT"

更新表根本不起作用,出现同样的错误。

好,所以我知道我不能选择可能同时更改的内容。但是我正在更新,同时也在检查是否未引用相同的行。

那我想念什么?

4 个答案:

答案 0 :(得分:2)

关系数据库中的邪恶冗余,您会尝试引入它。

更多正确关系方法将是定义没有IS_PARENT列的表。

select * from my_parent order by id;
        ID  ID_PARENT
---------- ----------
         1           
         2           
         3          2

...并在访问视图

中添加冗余列
create view V_MY_PARENT  as
select a.ID, a.ID_PARENT,
case when exists (select null from my_parent where ID_PARENT = a.ID) then 1 else 0 end as IS_PARENT
from my_parent a
order by a.ID;

要获取所有非父母,请访问视图

select * from V_MY_PARENT
where is_parent = 0;

        ID  ID_PARENT  IS_PARENT
---------- ---------- ----------
         1                     0
         3          2          0

如果您想实现冗余(例如出于性能原因),请使用MATERIALIZED VIEW

使用这种方法,您不会不结束,将父母分类为没有父母,反之亦然,这在您的设计中很有可能。

答案 1 :(得分:1)

只需替换

Invalid input found at row 2 of ... "Row parsing resulting in unexpected label name."

UPDATE "table"
   SET id_parent = 1
 WHERE id = :new.id_parent;

例如,避免针对if :old.id = :new.id_parent and updating then :new.id_parent := 1; end if; 在同一“表”中使用DML。

P.S。作为ORA-04091作为保留关键字,我替换为table

答案 2 :(得分:1)

错误原因是您正在更新尚未插入的行。

您可以使用:new关键字更新要插入的数据。

所以解决方案将用

替换更新语句
:new.is_parent := 1

答案 3 :(得分:1)

在这里出现ORA-04091(表正在变异,触发器可能看不到它),因为您在“ TABLE”上定义了一个BEFORE触发器,并且您试图在触发器主体中更新“ TABLE”。 Oracle不允许这样做,因为它可能导致触发器循环(即,如果允许,则您的程序可以执行导致触发器触发的语句;在触发器的主体内执行导致触发器触发的语句;在内部在触发器的主体中执行一条语句导致触发器触发;在触发器的主体中执行一条语句导致触发器触发;在触发器的主体中执行一条语句导致触发器触发;等等)。因此,您不允许在BEFORE触发器中执行此操作。最简单的解决方法是将触发器更改为AFTER触发器。换句话说,将BEFORE INSERT OR UPDATE ON table更改为AFTER INSERT OR UPDATE ON table。在这种特殊情况下,似乎没有问题,但是我不知道您在表上有哪些约束可能使此问题无法接受。试试看。