我试图创建一个控制自引用表中循环的触发器。 不幸的是我遇到了错误。谁能告诉我我做错了什么?
CREATE OR REPLACE TRIGGER CHECK_CYCLE
BEFORE INSERT OR UPDATE ON DEPARTMENTS
FOR EACH ROW
BEGIN
IF :NEW.PARENT_ID =
(SELECT ID, PARENT_ID, NAME, LEVEL,
CONNECT_BY_ISLEAF AS ISLEAF,
PRIOR NAME AS PARENT_NAME,
CONNECT_BY_ROOT NAME AS ROOT
FROM DEPARTMENTS
START WITH PARENT_ID IS NULL
CONNECT BY PRIOR ID = PARENT_ID)
THEN
RAISE_APPLICATION_ERROR(20000, 'Sorry.');
END IF;
END;
Error(9,25): PLS-00103: Encountered the symbol "AS" when expecting one of the following:
, from
答案 0 :(得分:0)
这似乎是解析器变得困惑。如果您完全删除AS ROOT
,则会恢复为预期的PLS-00405: subquery not allowed in this context
错误。不知道它为什么会混淆 - 你可以独立运行那个查询 - 但是因为你不能做你正在尝试的事情,所以可能不值得担心太多。
您的子查询也有三列并且将返回多行,因此与当前行的标量PARENT_ID进行比较是行不通的。您可以单独运行子查询,并将您真正感兴趣的值选择到局部变量中,然后检查它。
但是你有一个before-insert触发器,你正在查询触发器所针对的表,所以当你尝试插入时,你会得到一个变异表异常(或者至少,如果你试图插入多行立刻)。
如果我理解您要实现的目标,则可以使用后插入触发器:
create or replace trigger check_cycle
after insert or update on departments
declare
l_hascycle pls_integer;
begin
select max(connect_by_iscycle)
into l_hascycle
from departments
start with parent_id is null
connect by nocycle prior id = parent_id;
if l_hascycle = 1 then
raise_application_error(-20000, 'Sorry.');
end if;
end;
/
这使用CONNECT_BY_ISCYCLE pseudocolumn,如果没有循环,则所有行都为零,而对于任何循环的列,都为{0}。选择并检查max()
,这意味着您在本地l_hascycle
变量中只有一个值0或1,您可以使用它来决定是否抛出异常。
在现有的非循环数据之上插入几个测试行,例如:
ID PARENT_ID NAME
---------- ---------- ------------
1 Test
2 1 Test
...第一个新行正常,第二个行会导致循环:
insert into departments (id, parent_id, name) values (3, 2, 'OK');
1 row inserted.
insert into departments (id, parent_id, name) values (2, 3, 'Cycles');
ORA-20000: Sorry.
ORA-06512: at "SCHEMA.CHECK_CYCLE", line 11
ORA-04088: error during execution of trigger 'SCHEMA.CHECK_CYCLE'
第一个插入仍然有效(但尚未提交),第二个插入被隐式回滚:
select * from departments:
ID PARENT_ID NAME
---------- ---------- ------------
1 Test
2 1 Test
3 2 OK
由于start with
子句,这不会捕获未连接到现有树的数据中的循环,最终在空父项中;因此,对于数据的示例,您可以插入4,5和5,4而不会引发异常,因为没有从4或5到具有空父级的行的路由。但这是一个不同的问题,你可能想要单独测试。
它也无法查看其他会话中未提交更改的数据,因此两个会话可以同时插入独立有效的行,但一旦提交这些行仍然会形成一个循环。
答案 1 :(得分:-1)
我想我在你的查询中发现了错误,就在这一行 -
CONNECT_BY_ROOT NAME AS ROOT
未正确构建。 尝试将其更改为
CONNECT_BY_ROOT AS ROOT