2016-03-22 88 views
0

我试图创建控制在自引用表周期触发。 不幸的是我有一个错误。谁能告诉我我做错了什么?触发与子查询

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

你有没有尝试从查询中删除所有“为” S? – Hawk

+0

当然。没有帮助。 –

+0

你应该问自己这个部分是否需要在你的查询中,因为它没有在任何地方使用(据我所知)。 此外 - 你应该失去'new'关键字旁边的冒号,这是没有必要的。 –

回答

0

这似乎是解析器感到困惑。如果完全删除AS ROOT,则会恢复为预期的PLS-00405: subquery not allowed in this context错误。不知道它为什么会混淆 - 你可以单独运行这个查询 - 但是因为你不能做你正在尝试的东西,所以它可能不值得担心太多。

你的子查询也有三列,并且将返回多行,因此与当前行的标量PARENT_ID是行不通的比较。你可以单独运行你的子查询,并选择你真正感兴趣的值到一个局部变量中,然后检查它。

但是,您有一个before-insert触发器,并且您正在查询触发器违反的表,因此无论如何,当您尝试插入时(或者至少,如果尝试插入多个行一次)。

如果我理解你想达到什么样的,你可以使用后,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,这将是所有行零,如果没有自行车,和一个循环的任何列。选择和检查的该max()意味着你将在当地l_hascycle变量具有0或1的单个值,你可以用它来决定是否抛出异常。

上插入现有非循环的数据,如顶部的几个测试行:

 ID PARENT_ID NAME  
---------- ---------- ------------ 
     1   Test   
     2   1 Test   

...其中第一个新行是OK,第二会导致一个循环:

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到具有空父母的行的路由。但这是一个不同的问题,你可能需要单独测试。

这还不能看到其他会话未提交的修改数据,所以它可能是两个会议同时插入均有效,但仍然会形成一次循环都被提交的行。

+0

感谢您的决定。 –

+0

但是,当我尝试测试它时,您的触发器不起作用。据我了解,这后插入触发器搜索表中的现有周期。我使用了你的示例数据:insert into departments values(5,2,'OK');插入部门值(2,5,'Cycles');在这种情况下,变更承诺成功。 –

+0

插入后触发器可以查看刚插入的未提交数据。 (虽然它看不到来自其他会话的未提交的更改,但它不是万无一失的)。如果你已经创建了触发器并且从同一个会话中进行了两次插入,那么我看不到为什么它不会触发并抛出异常。除非您没有将5或2的整个层次结构备份到其父项为空的标识 - 如果该对是孤立的,那么'start with'子句将使查询停止查看这些行。 –

-1

我想我找到了错误的查询,这是该行 -

CONNECT_BY_ROOT NAME AS ROOT 

这是不正确建立。 尝试将其更改为类似

CONNECT_BY_ROOT AS ROOT 
+0

谢谢你的回答,但CONNECT_BY_ROOT NAME AS ROOT是正确的。如果删除AS我得到新的错误:错误(5,3):PL/SQL:语句被忽略 错误(6,3):PLS-00405:子查询不允许在这种情况下 –

+0

我真的不知道它是如何正确的,你有尝试过使用我的更正吗? –

+0

是的,我试过了。子查询在触发器外部使用时正常工作。 –