2012-06-11 169 views
5

我們在我們的應用程序中需要存儲引用以供以後訪問。數據庫歷史化

示例:用戶可以一次提交發票,並且該發票包含的所有參考(客戶地址,計算的金額,產品描述)和計算應該隨時間存儲。

我們需要以某種方式保存參考文獻,但如果例如產品名稱更改?所以我們需要複製一切,以便以後記錄下來,而不會受到未來變化的影響。即使產品被刪除,他們也需要在發票存儲後再進行審覈。

這裏關於數據庫設計的最佳實踐是什麼?即使最靈活的方法是什麼當用戶想要稍後編輯他的發票並從數據庫恢復它?

謝謝!

回答

9

下面是做到這一點的一種方法:

enter image description here

本質上說,我們從來沒有修改或刪除現有的數據。我們通過創建新版本來「修改」它。我們通過設置DELETED標誌來「刪除」它。

例如:

  • 如果產品的價格變化,我們插入新行PRODUCT_VERSION而舊訂單保持連接到舊PRODUCT_VERSION和舊價格。
  • 當買家更改地址時,我們只需在CUSTOMER_VERSION中插入新行並將新訂單鏈接到該行,同時將舊訂單鏈接到舊版本。
  • 如果產品被刪除,我們不會真正刪除它 - 我們只需設置PRODUCT.DELETED標誌,這樣歷史上爲該產品所做的所有訂單就會保留在數據庫中。
  • 如果客戶被刪除(例如,因爲他(他)要求取消註冊),請設置CUSTOMER.DELETED標誌。

注意事項:

  • 如果產品的名稱必須是唯一的,不能在聲明上述模型執行。您需要將PRODUCT_VERSION中的NAME「升級」爲PRODUCT,並將其設置爲關鍵字,並放棄「發展」產品名稱的權限,或者僅對最新的PRODUCT_VER(可能通過觸發器)實施唯一性。
  • 客戶的隱私存在潛在的問題。如果客戶從系統中刪除,則可能需要從數據庫中物理刪除其數據,只需設置CUSTOMER.DELETED就不會那樣做。如果這是一個問題,要麼刪除所有客戶版本中的隱私敏感數據,要麼將現有訂單從實際客戶斷開,並將其重新連接到特殊的「匿名」客戶,然後物理刪除所有客戶版本。

該模型使用了很多識別關係。這導致了「胖」的外鍵,並且可能存在一些存儲問題,因爲MySQL不支持前沿索引壓縮(不像Oracle),但另一方面,PK上的這種集羣可能是有益的表現。另外,JOIN不需要。

與非識別,關係和代理鍵等效模型是這樣的:

enter image description here

+0

要擁有唯一的產品名稱,您可以添加一個僅包含產品名稱的表格,其中名稱是pk,並鏈接到PRODUCT_VERSION的表格 –

+0

@OweJessen當然,您可以使用唯一的NAME包含LATEST_PRODUCT_VERSION表,但不包含算作「聲明式」解決方案,因爲在創建新產品版本時,您需要手動插入和刪除該表中的行。除非您使用的DBMS既可以自動更新實例化視圖,也可以實現它們的唯一性(例如MS SQL Server的索引視圖),因此DBMS本身會爲您維護LATEST_PRODUCT_VERSION。 –

1

您可以在產品表中添加一列,指示是否正在銷售。然後,當產品被「刪除」時,您只需設置標誌,使其不再作爲新產品提供,但您保留數據以備將來查找。

要處理名稱更改,您應該使用ID來引用產品,而不是直接使用名稱。

+0

,如果他想在歷史上包括產品名稱,因爲它是當它是第二點,也不會幫他出售。比如說,直到1988年,同樣的ItemId適用於「可口可樂」,然後才適用於「可口可樂經典」,並且他想知道在老的訂單上它被稱爲「可樂」。你的建議是他試圖避免的 - 他使用規範化數據生成的任何報告都將在1988年之前的所有訂單上顯示「可口可樂經典」。 – David

+0

真的。解決這個問題的一種方法是爲名稱更改創建一個新產品,或者添加另一個表來跟蹤產品名稱(該方法接近@Branko Dimitrijevic的解決方案)。 – whrrgarbl

1

您正面臨的問題是,我相信您知道,數據庫規範化的結果。解決此問題的方法之一可以從商業智能技術中獲取 - 將數據歸檔爲Data Warehouse中的非歸一化狀態。

標準化數據:

  • Orders表
    • 的OrderId
    • 客戶編號
  • 客戶表
    • 客戶編號
  • 項目表
    • 項目Id
    • ITEMNAME
    • ITEMPRICE
  • OrderDetails表
    • ItemDetailId
    • 的OrderId
    • 項目Id
    • ItemQty

當查詢和存儲反規範化,數據倉庫表看起來像

  • 的OrderId
  • 客戶ID
  • 客戶名稱
  • CustomerAddress
  • (其它客戶字段)
  • ItemDetailId
  • 項目Id
  • ITEMNAME
  • ITEMPRICE
  • (其他的OrderDetail和項字段)

通常,要麼是某種按計劃將數據從規範化數據提取到數據倉庫的預定作業,或者如果您的計劃作業符號允許,可以在訂單達到某個狀態時完成。 (如發貨)可能是記錄在每次狀態更改時都會存儲(有一個名爲OrderStatus的字段會增加當前狀態),因此完全取消規範化的數據適用於oprder /履行過程的每個步驟。何時以及如何將數據歸檔到倉庫中會根據您的需求而有所不同。


上面涉及很多開銷,但我知道的另一種常見方法會帶來更多開銷。

另一種方法是將表格設置爲只讀。如果客戶想要更改他們的地址,則不需要編輯他們現有的地址,而是插入新的記錄。

因此,如果我的地址是AddressId 12,當我在Jamnuary的網站上進行第一次訂購時,那麼我將在7月4日移動,我會得到一個與我的帳戶綁定的新AddressId。 (說AddressId 123123因爲你的網站是非常成功的,並已吸引了一噸的客戶)。

令我7月4日之前將地點理想有AddressId與它們相關的12,並放置7月4日或之後的訂單已經AddressId 123123.

對每個需要保留歷史數據的表格重複該模式。


我確實有第三種方法,但搜索它很困難。我只在一個應用程序中使用它,並且在單個實例中實際運行得非常好,它具有與特定時間點完全相同的重構數據的特定業務需求。除非我有類似的業務需求,否則我不會使用它。

在特定狀態下,將數據序列化爲Xml文檔或其他可用於重建數據的文檔。這使您可以保存數據,因爲它在序列化時保留原始表結構和關係。

+0

是的......什麼@大衛斯特拉頓說...刪除我更多的口頭回答同一個想法。 – GDP

+0

@Greg P - 我打算給你投票。你的答案更簡潔,而且仍然相關。 – David

+0

爲後代添加了它......當有更簡潔的答案時,不喜歡重複,謝謝。 – GDP

1

你在純粹主義和實用主義之間展開了一場永恆的辯論。

從數據庫的規範化角度來看,您應該「保留」所有相關數據。換句話說,如果產品名稱發生變化,請保存更改的日期,以便您能夠及時回溯並使用該產品名稱以及當天存在的所有其他數據重建發票。

「de」標準化方法是將該發票視爲「時刻」,並在相關表格中記錄當天的實際數據。這種方法可以讓您在沒有任何依賴關係的情況下提取發票,但是您無法從頭開始重新創建發票。

0

當您有時間敏感數據時,您可以使用產品和客戶表等信息作爲查找表,並將信息直接存儲在您的訂單/訂單細節表中。

因此,訂單表可能包含客戶名稱和地址,詳細信息將包含有關產品的所有相關信息,包括特別是價格(除了初始查找時,您絕對不希望依賴產品表獲取價格信息命令)。

這不是反規範化,數據隨時間變化,但您需要歷史數據,因此您必須在創建記錄時存儲數據,否則將失去數據完整性。你不希望你的財務報告突然顯示你去年賣出了30%,因爲你有價格更新。這不是你賣的東西。

+1

「這不正常化......」是的。在關係系統中,重複數據意味着「具有相同含義的相同值」。這裏的值可能是相同的,但含義不同。 (當前價格,例如,與訂單時的價格相比。) –