【问题标题】:Oracle 11g: Compound triggers with an "on delete cascade" constraintOracle 11g:具有“on delete cascade”约束的复合触发器
【发布时间】:2015-11-07 22:38:20
【问题描述】:

在 Oracle (11.2.0.1) 中,我有 2 个表,假设 AB 之间存在引用约束(“删除级联”)。 AB 的“父级”。

create table A (id number(9) not null primary key);
create table B (id number(9) not null primary key, parent_id not null references A(id) on delete cascade);

子表在删除时也有一个相关的复合触发器(主要目的是避免在一系列复杂的完整性检查中出现突变错误)。

create or replace trigger trg_b
for delete on b
compound trigger
  type b_table is table of b%rowtype;
  b_collection  b_table := b_table();

  before each row is
  begin
    b_collection.extend;
    b_collection(b_collection.last).id      := :old.id;
    b_collection(b_collection.last).parent_id := :old.parent_id;
  end before each row;

  after statement is
  begin
  for i in b_collection.first .. b_collection.last loop
    --logging into another table in an autonomous transaction
  end loop;
 b_collection.delete;
 end after statement; 

end trg_b;

使用这些表的算法之一如下:

  1. 所有具有特定 parent_id 的行都将从 B 中删除。
  2. 额外检查是否没有剩余子行。
  3. 从父 A 中删除此特定 ID。

在我们的生产环境中,第 3 步在字符串 b_collection.first .. b_collection.last 处生成异常 ORA-06502: PL/SQL: numeric or value error。这意味着从父表中删除会导致触发子表上的触发器,即使没有可能的子行受到影响。这可能是有道理的,因为触发触发器时集合不存在并且第一个和最后一个集合索引为 NULL,但我无法重现这种行为。

在我们的开发环境中,当我尝试从 B 中删除一些不存在的 id/parent_id 时,我只会得到一个 ORA-06502。当我尝试从 A 中删除一些存在的(没有子行)或不存在的 id 时,我没有收到任何错误 - 0 行被删除,0 个子行受到影响。从记录中我可以告诉孩子相关的触发器在这种情况下甚至没有被触发。为什么不呢?

任何想法为什么所描述的行为可能如此不同?我错过了什么?

【问题讨论】:

    标签: oracle plsql oracle11g triggers


    【解决方案1】:

    我想通了。 11.2.0.1 的Bug 8830338 - BEFORE and AFTER STATEMENT not executed in compound trigger for DELETE CASCADE (Doc ID 8830338.8) 参与了我的触发器乐趣。它声称自补丁集 11.2.0.2 以来已修复,因为这种带有 after 语句的复合触发器应该被触发并以糟糕的方式结束。

    【讨论】:

      【解决方案2】:

      每当您的集合为空时,您都会收到 ORA-06502: PL/SQL: numeric or value error 异常:

      SQL> declare
        2    type t is table of number;
        3    numtab t := t();
        4  begin
        5    for i in numtab.first..numtab.last loop
        6      null;
        7    end loop;
        8  end;
        9  /
      declare
      *
      ERROR at line 1:
      ORA-06502: PL/SQL: numeric or value error
      ORA-06512: at line 5
      

      除非有任何方式最终导致集合变得稀疏(即从中间删除一些成员),否则我会使用此循环:

        1  declare
        2    type t is table of number;
        3    numtab t := t();
        4  begin
        5    for i in 1..numtab.count loop
        6      null;
        7    end loop;
        8* end;
      SQL> /
      
      PL/SQL procedure successfully completed.
      

      否则,您需要在处理之前测试集合是否为空(当已知集合很密集时,这对我来说总是过于繁琐):

        1  declare
        2    type t is table of number;
        3    numtab t := t();
        4  begin
        5    if numtab.count > 0 then
        6      for i in numtab.first..numtab.last loop
        7        null;
        8      end loop;
        9    end if;
       10* end;
      SQL> /
      
      PL/SQL procedure successfully completed.
      

      【讨论】:

      • 感谢您的快速回复!我知道如何正确处理空集合,但真正困扰我的是触发器的魔力。
      猜你喜欢
      • 2013-04-19
      • 2013-01-30
      • 2012-01-06
      • 1970-01-01
      • 2010-09-08
      • 2011-09-21
      • 2014-09-26
      • 2012-05-23
      • 1970-01-01
      相关资源
      最近更新 更多