2012-09-09 16 views
1

好吧,我有兩個表 - ORDERS和ORDERLINES - 它們基本上都有相同的問題,每個表都有觸發器來解決問題。問題在於,除了具有表級唯一性的PK之外,在名爲RECID的字段中,還有另一個字段RECNO,它需要與另一個字段相關而是唯一的。ORACLE在一次觸發中出現突變表錯誤,但不是另一次;爲什麼?

的表FK關係如下:

ORDERS.WAREHOUSEID > WAREHOUSES.CUSTOMERID > CUSTOMERS

ORDERSLINES.ORDERID > ORDERS

ORDERSORDERSLINES我有BEFORE INSERT觸發分配特定的領域唯一RECNO
ORDERS,RECNO需要在CUSTOMERS記錄的範圍內是唯一的。
ORDERLINES,RECNO需要在ORDERS記錄的範圍內是唯一的。

ORDERS上的觸發器工作得很好。當插入新訂單時,會爲其所屬的客戶分配下一個唯一RECNO

在另一方面上ORDERLINES觸發,這應該它屬於訂單中指定的下一個唯一RECNO,拋出可怕{ORA-04091:表ORDERLINES被突變,觸發/功能可能無法看到它}異常。

這裏是工作的觸發器:

CREATE OR REPLACE TRIGGER ORDERS_BI 
BEFORE INSERT ON ORDERS 
FOR EACH ROW 
DECLARE 
    CUSTID WAREHOUSES.CUSTOMERID%TYPE; 
BEGIN 
    SELECT MIN(CUSTOMERID) INTO CUSTID FROM WAREHOUSES 
     WHERE NVL(WARE_ID, '-') = NVL(:NEW.WAREHOUSEID, '-'); 

    SELECT NVL(MAX(RECNO), 0) + 1 
     INTO :NEW.RECNO 
     FROM deploy.ORDERS O 
     LEFT JOIN deploy.WAREHOUSES W 
      ON NVL(W.REC, '-') = NVL(O.WAREHOUSEID, '-') 
     WHERE NVL(W.CUSTOMERID, '-') = NVL(CUSTID, '-'); 
END; 

這裏是不會觸發工作:

CREATE OR REPLACE TRIGGER ORDERLINES_BI 
BEFORE INSERT ON ORDERLINES 
FOR EACH ROW 
DECLARE 
    nORDERID ORDERLINES.ORDERID%TYPE; 
BEGIN 
    SELECT MIN(ORDERID) INTO nORDERID FROM REVORDERS 
     WHERE ORDERID = :NEW.ORDERID; 

    SELECT NVL(MAX(RECNO), 0) + 1 
     INTO :NEW.RECNO 
     FROM deploy.ORDERLINES L 
     LEFT JOIN deploy.ORDERS O 
     ON O.ORDERID = L.ORDERID 
     WHERE O.ORDERID = nORDERID; 
END; 

有人可以解釋爲什麼第一個作品,並第二個不? 有沒有什麼方法可以重新寫第二個讓它工作?

+0

可能重複的[ORACLE更新後觸發器:解決ORA-04091突變表錯誤](http://stackoverflow.com/questions/6915325/oracle-after-update-trigger-solving-ora-04091-mutating-table -error),[Oracle觸發器 - 突變表的問題](http://stackoverflow.com/q/2138363) – Sathya

+0

觸發表ORDERS上的ORDERS_BI不會從表ORDERS中讀取。表ORDERLINES上的ORDERLINES_BI觸發器從表ORDERLINES中讀取。 –

+0

不重複。這個問題是關於什麼是基本的突變錯誤。我做了一個更正。請注意,IS工作的觸發器實際上是從目標表中選擇的。 ***這是我的困惑來自哪裏。 – eidylon

回答

2

您正在收到該錯誤,因爲第二個觸發器正在嘗試讀取正在修改的表。當父表上的觸發器引起對引用外鍵的子表的插入時,也會發生這種情況。 作爲快速創建視圖並嘗試使用而不是觸發器。 另請參閱Tom's示例如何處理變異問題。 此外,如果離開第二個觸發器,任何插入your_table選擇..從表將引發突變錯誤。例如:

該插入將工作

insert into ORDERLINES(column1, column2... columnN) 
    values(val1, val2,..., valN) 

但是這一次的習慣。

insert into ORDERLINES(column1, column2... columnN) 
    select val, val..val from table 
4

我先看了你的代碼,而不是你的解釋。我的第一個想法是「這個人試圖僞造一個sequence」。這顯然不是你的問題的答案,但這是你首先陷入困境的原因。

當你有僞造序列的問題時,一個明顯的解決方案是使用一個真正的解決方案。

As Nicholas has already noted當您嘗試從觸發器觸發的表讀取時發生ORA-04091。有很多方法可以避免這種情況,其中大部分避免嘗試做些微不足道的事情。但是,它們不會影響錯誤的根本原因;那是你在做什麼錯了。這個錯誤通常是表明一個或兩個的兩件事情:

  1. 你把太多的邏輯爲觸發器
  2. 你的數據模型是有缺陷的。

第一種解決方案是將邏輯移動到一個包,它具有移除一層混淆的附加好處。第二種解決方案是正確地標準化數據庫。

就你而言,從你提供的信息看,你的數據模型似乎沒問題,但正如我所說的,我不同意實現。

這讓你有四個選項,以解決您的問題,我會做我的細節,以便他們

  1. 刪除您的觸發器。
  2. 用序列替換當前的邏輯。
  3. 將所有觸發邏輯移除到一個過程中。
  4. 解決你的錯誤。

我不打算討論第3點,因爲你可以自己做。尼古拉斯已經部分覆蓋了第4點,我不會主張我不同意的東西。這使點1和2,你說

訂單,RECNO需要一個客戶 記錄的領域中是唯一的。

這不是你如何實現它的。您的代碼在CUSTOMERS記錄的範圍內連續RECNOORDERSORDERLINES的主鍵都是根據定義記錄的CUSTOMERS記錄的範圍內唯一。

這本身就意味着選項1最適合你。完全刪除觸發器;表的主鍵已經在做你需要的一切。這也使選項2失效;如果你添加一個序列,那麼它將基本上是一個單獨的主鍵。

我沒有理由認爲您需要訂單才能在每位客戶中連續唯一;爲什麼打擾呢?

+0

是的,你是對的......我試圖讓RECNO字段在相關領域內唯一連續。兩個表都有一個獨立的RECID列,它是主鍵,* IS *通過標準序列填充,如人們所期望的那樣。 RECNO是一個領域特定的連續數字。對於ORDERLINES,原因應該是顯而易見的......每條線都不管其PK值是否爲特定順序的第1,2,... n行。 對於訂單一,它只是這個系統的業務需求。對於每個客戶,無論主鍵值是什麼,他們都會知道他們的訂單爲1,2,... n。 – eidylon

+0

我沒有得到的是爲什麼ORDERS觸發器沒有突變錯誤,而ORDERLINES失敗。如果我能弄清楚ORDER的工作原理,我應該能夠讓ORDERLINES工作。 – eidylon

相關問題