得到一个表,该表本身具有一个键:
+----+-----------+-----------+
+ ID + ID_PARENT + IS_PARENT +
+----+-----------+-----------+
+ 1 + (null) + 0 +
+ 2 + (null) + 1 +
+ 3 + 2 + 0 +
+----+-----------+-----------+
如您所见,ID 1
是独立的,3
是2
的子代。
现在我想要一个触发器,在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"
更新表根本不起作用,出现同样的错误。
好,所以我知道我不能选择可能同时更改的内容。但是我正在更新,同时也在检查是否未引用相同的行。
那我想念什么?
答案 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
。在这种特殊情况下,似乎没有问题,但是我不知道您在表上有哪些约束可能使此问题无法接受。试试看。