2015-04-07 40 views
3

對不起,我的英語。插入或刪除後的Oracle觸發器

我有2個表:

Table1 
id 
table2_id 
num 
modification_date 

Table2 
id 
table2num 

我想打一個觸發器,它後插入或刪除Table1Table2.table1lastnum更新的最後一個值num

我的觸發器:

CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG 
    AFTER INSERT OR DELETE ON table1 
    FOR EACH ROW 
BEGIN 
    IF INSERTING then 

    UPDATE table2 
    SET table2num = :new.num 
    WHERE table2.id = :new.table2_id; 

    ELSE 

    UPDATE table2 
    SET table2num = (SELECT num FROM (SELECT num FROM table1 WHERE table2_id = :old.table2_id ORDER BY modification_date DESC) WHERE ROWNUM <= 1) 
    WHERE table2.id = :old.table2_id; 

    END IF; 

END TABLE1_NUM_TRG; 

但刪除後Table1我有錯誤:

ORA-04091: table BD.TABLE1 is mutating, trigger/function may not see it 
ORA-06512: at "BD.TABLE1_NUM_TRG", line 11 
ORA-04088: error during execution of trigger 'BD.TABLE1_NUM_TRG' 

我在做什麼錯?

+1

你試圖運行從你'DELETE'ing一排桌子的'SELECT'聲明觸發之前定義爲delete.Try。 –

+0

谷歌的錯誤代碼ORA-04091,你會得到很多答案。 –

回答

5

你碰到的是經典的「變異表」異常。在ROW觸發器中,Oracle不允許您針對定義觸發器的表運行查詢 - 因此它是針對導致此問題的觸發器的DELETING部分中的TABLE1的SELECT

有幾種方法可以解決這個問題。也許在這種情況下,最好是使用複合觸發器,它看起來是這樣的:

CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG 
    FOR INSERT OR DELETE ON TABLE1 
COMPOUND TRIGGER 
    TYPE NUMBER_TABLE IS TABLE OF NUMBER; 
    tblTABLE2_IDS NUMBER_TABLE; 

    BEFORE STATEMENT IS 
    BEGIN 
    tblTABLE2_IDS := NUMBER_TABLE(); 
    END BEFORE STATEMENT; 

    AFTER EACH ROW IS 
    BEGIN 
    IF INSERTING THEN 
     UPDATE TABLE2 t2 
     SET t2.TABLE2NUM = :new.NUM 
     WHERE t2.ID = :new.TABLE2_ID; 
    ELSIF DELETING THEN 
     tblTABLE2_IDS.EXTEND; 
     tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.TABLE2_ID; 
    END IF; 
    END AFTER EACH ROW; 

    AFTER STATEMENT IS 
    BEGIN 
    IF tblTABLE2_IDS.COUNT > 0 THEN 
     FOR i IN tblTABLE2_IDS.FIRST..tblTABLE2_IDS.LAST LOOP 
     UPDATE TABLE2 t2 
      SET t2.TABLE2NUM = (SELECT NUM 
           FROM (SELECT t1.NUM 
             FROM TABLE1 t1 
             WHERE t1.TABLE2_ID = tblTABLE2_IDS(i) 
             ORDER BY modification_date DESC) 
           WHERE ROWNUM = 1) 
      WHERE t2.ID = tblTABLE2_IDS(i); 
     END LOOP; 
    END IF; 
    END AFTER STATEMENT; 
END TABLE1_NUM_TRG; 

複合觸發器允許每個時間點(BEFORE STATEMENTBEFORE ROWAFTER ROW,並且AFTER STATEMENT)進行處理。請注意,時間點總是按給定的順序調用。當執行適當的SQL語句(即INSERT INTO TABLE1DELETE FROM TABLE1)並觸發此觸發器時,要調用的第一個時間點將爲BEFORE STATEMENT,並且BEFORE STATEMENT處理程序中的代碼將分配PL/SQL表以容納一堆數字。在這種情況下,要存儲在PL/SQL表中的數字將是TABLE1的TABLE2_ID值。 (使用PL/SQL表來代替數組,因爲一個表可以容納不同數量的值,而如果我們使用了一個數組,我們必須事先知道需要存儲多少個數。我們無法預先知道特定語句會影響多少行,因此我們使用PL/SQL表)。當到達AFTER EACH ROW時間點,並且我們發現正在處理的語句是INSERT時,觸發器會繼續執行並對TABLE2執行必要的UPDATE,因爲這不會導致問題。但是,如果正在執行DELETE,觸發器會將TABLE1.TABLE2_ID保存到先前分配的PL/SQL表中。當最終到達AFTER STATEMENT時間點時,先前分配的PL/SQL表被遍歷,並且對於每個TABLE2_ID找到適當的更新。

Documentation here

分享和享受。

1

你必須使用兩個觸發器

CREATE OR REPLACE TRIGGER INS_TABLE1_NUM_TRG 
AFTER INSERT ON table1 
FOR EACH ROW 
BEGIN 
    UPDATE table2 
    SET table2num = :new.num 
    WHERE table2.id = :new.table2_id; 
END INS_TABLE1_NUM_TRG; 


CREATE OR REPLACE TRIGGER DEL_TABLE1_NUM_TRG 
BEFORE DELETE ON table1 
FOR EACH ROW 
BEGIN 
    UPDATE table2 
    SET table2num = (SELECT num FROM 
    (SELECT num FROM table1 WHERE table2_id = :old.table2_id 
    ORDER BY modification_date DESC) 
    WHERE ROWNUM <= 1) 
    WHERE table2.id = :old.table2_id; 
END DEL_TABLE1_NUM_TRG;