2017-08-17 91 views
0

目前我正在實現一個過程,該過程在模板外的某些相關表中創建了幾行。因此,我的過程包含SAVEPOINT後跟一些INSERT語句在不同的表上,以及一個Cursor用於在引用新創建的主鍵時將更多行插入到其他表中。帶有嵌套的原子事務BEFORE INSERT/UPDATE觸發器

每個這些表的定義了一個BEFORE INSERT/UPDATE觸發器,其目的到:

  • 獲得從序新的主鍵,如果沒有在INSERT語句中定義(有這樣的情況:我需要明確地設置主鍵稍後引用它在同一事務)
  • 設置一些默認值,如果他們是NULL
  • 設置審覈字段(last_change_date,last_change_user,等..)

交易失敗ORA-04091:表變異,觸發/功能可能無法看到它

我的理解,我可以解決此,通過在每個觸發器聲明PRAGMA自治事務,但我的交易將不再是原子,因爲它是所有這些數據集應該作爲一個整體創建/插入的要求,或者它們都不是。

那麼我在做數據庫設計時做錯了什麼?


UPDATE:這是觸發的代碼

CREATE TRIGGER TRG_AUFTRAG_B_IU 
    BEFORE INSERT OR UPDATE 
    ON AUFTRAG 
    FOR EACH ROW 
    BEGIN 
    IF INSERTING THEN 
    IF :new.id is NULL or :new.id = 0 THEN 
     SELECT SEQ_AUFTRAG.nextval into :new.id from dual; 
    END IF; 

    IF :new.nummer is NULL or :new.nummer = 0 THEN 
     SELECT nvl(MAX(NUMMER),0)+1 INTO :new.nummer FROM AUFTRAG WHERE EXTRACT(YEAR from DATUM) = EXTRACT(YEAR from :new.DATUM); 
    END IF; 

    --DEFAULT Values 
    IF :new.BETR_GRENZWERTE_RELEVANT is NULL THEN 
     SELECT 0 INTO :new.BETR_GRENZWERTE_RELEVANT FROM dual; 
    END IF; 

    IF :new.DOKUMENTE_ABGELEGT is NULL THEN 
     SELECT 0 INTO :new.DOKUMENTE_ABGELEGT FROM dual; 
    END IF; 

    IF :new.EXT_ORG is NULL or :new.EXT_ORG < 1 THEN 
     SELECT 1 INTO :new.EXT_ORG FROM dual; 
    END IF; 

    :new.ERSTELLT_VON := nvl(:new.ERSTELLT_VON,user); 
    :new.ERSTELLT_DATUM := nvl(:new.ERSTELLT_DATUM,sysdate); 
    END IF; 

    :new.GEAENDERT_VON := user; 
    :new.GEAENDERT_DATUM := sysdate; 
    END; 
+2

請向我們展示您的觸發器代碼。實際上你列出的動作對於觸發器來說是非常典型和有用的,它們應該沒有任何問題地工作。 –

+2

看起來你正在觸發器內的同一個表中執行一個'select'。根據你所說的觸發器需要做的事情,沒有任何「選擇」的要求。你說得對 - '編譯自主事務'不能解決這類問題。 –

+1

啊!我想我已經看到了什麼問題......這是:新:NUMMER部分是不是?這是爲了創建一個業務需求的識別號碼。它會在AFTER INSERT TRIGGER中做到這一點嗎? –

回答

1

你可以寫它更緊湊是這樣的:

CREATE TRIGGER TRG_AUFTRAG_B_IU 
    BEFORE INSERT OR UPDATE 
    ON AUFTRAG 
    FOR EACH ROW 
    BEGIN 
    IF INSERTING THEN 
     :new.id = NVL(NULLIF(:new.id, 0), SEQ_AUFTRAG.nextval); 

     --DEFAULT Values 
     :new.BETR_GRENZWERTE_RELEVANT := NVL(:new.BETR_GRENZWERTE_RELEVANT, 0); 
     :new.DOKUMENTE_ABGELEGT := NVL(:new.DOKUMENTE_ABGELEGT, 0); 

     IF :new.EXT_ORG is NULL or :new.EXT_ORG < 1 THEN 
      :new.EXT_ORG := 1; 
     END IF;  
     :new.ERSTELLT_VON := nvl(:new.ERSTELLT_VON,user); 
     :new.ERSTELLT_DATUM := nvl(:new.ERSTELLT_DATUM,sysdate); 
    END IF; 

    :new.GEAENDERT_VON := user; 
    :new.GEAENDERT_DATUM := sysdate; 
END; 

唯一的 「問題」 是這部分

IF :new.nummer is NULL or :new.nummer = 0 THEN 
    SELECT nvl(MAX(NUMMER),0)+1 INTO :new.nummer 
    FROM AUFTRAG 
    WHERE EXTRACT(YEAR from DATUM) = EXTRACT(YEAR from :new.DATUM); 
END IF; 

這一個你應該放入你的程序或在一個語句觸發器(即沒有FOR EACH ROW子句)如下:

CREATE TRIGGER TRG_AUFTRAG_B_A 
    AFTER INSERT ON AUFTRAG 
BEGIN 
    UPDATE 
     (SELECT ID, NUMMER, 
      ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR from DATUM) ORDER BY ID) as N 
     FROM AUFTRAG) 
    SET NUMMER = N 
    WHERE NUMMER IS NULL;   
END; 
+0

非常感謝!不僅爲了讓這些概念 - 變異表 - 對我來說很清楚,而且還用於提示代碼的改進。 –