2011-11-06 86 views
1

我正在學習大學課程的SQL和DB設計。賦予我們的一項任務是創建一個具有派生屬性的表格,這是一些子屬性的SUM。例如:如何防止將派生值(SUM)手動更新爲不正確的值

ORDERS 
orderID {PK} 
/orderTotal /* derived from SUM of child itemTotals */ 

ITEMS 
itemNo {PK} 
orderID {FK} 
itemTotal 

現在,我甚至不確定這是好的做法。從我在網上完成的一些閱讀中,派生值不應該被存儲,而是由用戶應用程序計算。我可以理解這種觀點,但在這種情況下,我的任務是存儲派生值,更重要的是通過觸發器保持其完整性,這對我來說是比較新的,所以我很享受使用它們。我還想象一下,在一些更復雜的情況下,保存處理時間真的值得存儲派生值。以下是我已經設置的不會給我帶來問題的安全措施:

插入新子項時更新父/訂單總計的觸發器。

刪除子項目時更新parent/orderTotal的觸發器。

當修改child itemTotal時更新parent/orderTotal的觸發器。

但是,還有另一個我想要的保障,我無法弄清楚如何完成。由於派生了父屬性/ orderTotal,因此不應手動修改它。如果有人試圖手動修改它(對於實際上不是正確的SUM的錯誤值),我想要(a)阻止他們這樣做或(b)一旦完成就將其恢復到其舊值。

哪種方法更好,哪些可能(以及如何)?我不知道如何完成前者,我試圖通過觸發器或約束來完成後者,但似乎都不合適。觸發器方法不斷給我ORA-04091錯誤試圖改變觸發表的觸發器。約束方法,我不認爲是合適的,因爲我不知道如何在約束檢查內做這樣的特定事情。

我在SQL Developer中使用Oracle SQL的方式。

謝謝!

+0

我會質疑在數據庫中使用觸發器來更新派生值之前,我會質疑存儲派生值的適當性的適當性。爲什麼不讓應用程序的業務邏輯直接設置該字段?這樣,如果有特定地點的稅收和/或運費等事情,則業務邏輯可以計算並存儲正確的派生值,並將這些值考慮在內。 – aroth

+0

@aroth:他把它描述爲一個DB類的賦值,所以沒有選擇在哪裏做。 – jmoreno

回答

3

「現在,我甚至不能確定thise是很好的做法。」

你的直覺是對的:這個不好的做法。出於某種原因,許多大學教授將學生設置爲編寫不良代碼的任務;如果他們至少說明這是不好的做法,並且不應該在現實世界中使用,那麼這不會太糟糕。但是,我認爲大多數教授對現實世界中的重要性掌握有限。 感嘆

Anyhoo,回答你的問題。有兩種方法來解決這個問題。其中一個就是使用觸發器來「糾正」即吞噬變化。這將是錯誤的,因爲用戶試圖修改該值可能會浪費大量時間試圖發現他們的變化爲什麼沒有堅持,沒有意識到他們正在違反業務規則。所以,拋出異常要好得多。

本示例使用Oracle語法,因爲我猜這就是您正在使用的。

create or replace trigger order_header_trg 
    before insert or update 
    on order_header for each row 
begin 
    if :new.order_total != :old.order_total 
    then 
     raise_application_error 
       (-20000, 'You are not allowed to modify the value of ORDER_TOTAL'); 
    end if; 
end; 

這種方法唯一的問題是,它會阻止你行插入ORDER_LINES然後推導ORDER_HEADER一個新的總。

這是非規範化總數爲壞習慣的原因之一。

的錯誤你得到 - ORA-04091 - 說 「變異表」。當我們試圖編寫一個從擁有觸發器的表中進行選擇的觸發器時,會發生這種情況。它幾乎總是指向一個糟糕的數據模型,一個不夠標準化的數據模型。這顯然是這種情況。

假設你被卡住的數據模型,唯一的解決方法是使用多個觸發器和包笨重的實現。互聯網提供了各種略有不同的解決方案:here is one

0

一個解決方案可能是將orderTotal的所有邏輯移入ORDERS表中的INSTEAD OF UPDATE觸發器。

的觸發你已經在ITEMS可以簡化更新ORDERS而不進行計算 - 設置orderTotal to 0 or something like that. The INSTEAD OF`觸發器將替代更新語句的運行。

如果嘗試手動更新訂單總額,則觸發器將觸發並重新計算現有值。

PS - 我沒有一個Oracle數據庫來測試,但是從我所知道的,一個INSTEAD OF觸發器會得到解決的ORA-04091錯誤 - aplologies如果這是錯誤的

編輯 雖然此解決方案可以在其他一些RDBMS系統中運行,但Oracle僅在視圖上支持觸發器INSTEAD OF

編輯2 如果分配允許的話,一個視圖可以是一個合適的解決這個問題,因爲這將使得能夠計算出的順序值列。

+0

感謝您的回覆......我嘗試了類似於自己的事情,但似乎INSTEAD OF只適用於視圖而不適用於表格。但我不是專家,所以也許我做錯了什麼。 – The111

+0

@Johnson - Ahh - 對不起 - 在文檔中錯過了。您可以通過將'ORDERS'表作爲一個視圖並讓最終用戶訪問該視圖而不是使用Oracle安全性的基礎表來解決這個問題嗎? –

+0

@Johnson - 是否使用觸發器?一個視圖可能是一個使用觸發器的更好的解決方案。 –