触发子查询

时间:2016-03-22 09:47:33

标签: oracle plsql triggers

我试图创建一个控制自引用表中循环的触发器。 不幸的是我遇到了错误。谁能告诉我我做错了什么?

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 

2 个答案:

答案 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