2015-08-14 67 views
0

在Oracle(11.2.0.1),我有2個表,假設觸發一個與他們之間的借鑑約束( 「ON DELETE CASCADE」) 。 AB的「父」。的Oracle 11g:化合物與 「ON DELETE CASCADE」 約束

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所有行將從被刪除。如果沒有子行留下
  2. 額外的檢查。
  3. 從父母刪除此特定ID A

在我們的生產環境中,第3步在字符串b_collection.first .. b_collection.last處生成異常ORA-06502: PL/SQL: numeric or value error。這意味着從父表中刪除導致觸發子表上的觸發器,即使沒有可能的子行受到影響。這可能是有道理的,因爲觸發器被觸發並且第一個和最後一個收集索引爲NULL時,集合不存在,但是我不能再現這種行爲。

在我們的開發環境中,當我試圖從B中刪除一些不存在的id/parent_id時,我只能得到ORA-06502。在它轉彎時,我嘗試刪除一些存在的(無子行)或不存在的ID從一個我沒有得到任何錯誤 - 0行刪除,0影響子行。從日誌記錄我可以告訴孩子相關的觸發器甚至在這種情況下不被解僱。爲什麼不?

任何想法,爲什麼所描述的行爲可能會如此不同?我錯過了什麼?

回答

2

我想通了。用於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以來一直是固定的,因爲這樣一個帶有後置語句的複合觸發器應該已經被解僱並最終以糟糕的方式結束。

1

您將得到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. 
+0

感謝您的快速回復!我知道如何正確處理空集合,但真正困擾我的是帶觸發器的魔法。 – Ennike