2010-10-06 101 views
114

有些同事和我討論了存儲歷史數據的最佳方式。目前,對於某些系統,我使用單獨的表來存儲歷史數據,並且保留當前活動記錄的原始表。所以,假設我有桌子FOO。在我的系統下,所有活動記錄都將以FOO形式出現,所有歷史記錄將以FOO_Hist形式出現。用戶可以更新FOO中的許多不同字段,因此我想對所有更新的內容都進行準確的說明。 FOO_Hist與FOO保持完全相同的字段,但自動遞增的HIST_ID除外。每次FOO更新時,我都會在FOO_Hist中執行一條插入語句,類似於:insert into FOO_HIST select * from FOO where id = @id如何存儲歷史數據

我的同事說這是不好的設計,因爲由於歷史原因,我不應該有一張表的確切副本,應該用一個標誌表示它是出於歷史目的而插入到活動表中的另一條記錄。

有處理歷史數據存儲的標準嗎?在我看來,我不想將我的活躍記錄與我所有的歷史記錄混在一張表中,考慮到它可能超過一百萬條記錄(我在長期考慮)。

你或你的公司如何處理這個問題?

我正在使用MS SQL Server 2008,但我想保留通用和任意DBMS的答案。

回答

50

直接在操作系統內支持歷史數據將使您的應用程序比其他情況更加複雜。一般來說,我不會推薦這樣做,除非您有嚴格的要求來操縱系統中的歷史記錄版本。

如果你仔細觀察,對歷史數據的大多數需求分爲兩類:

  • 審計日誌:這是最好與審計表來完成。編寫一個工具可以非常容易地生成腳本,通過從系統數據字典中讀取元數據來創建審計日誌表和觸發器。這種類型的工具可以用來改進大多數系統上的審計日誌記錄。如果要實施數據倉庫,也可以使用此子系統來更改數據捕獲(請參閱下文)。

  • 歷史報表:報告歷史狀態,按原樣處理或隨時間變化的分析報告。通過查詢上述類型的審計日誌表可以滿足簡單的歷史報告要求。如果您有更復雜的需求,那麼爲報告實施數據集市可能更經濟,而不是嘗試將歷史直接集成到運營系統中。

    緩慢變化的尺寸是迄今爲止用於追蹤和查詢歷史狀態的最簡單機制,並且大部分歷史追蹤都可以實現自動化。通用處理程序並不難寫。通常,歷史報告不必使用最新的數據,因此分批刷新機制通常很好。這使您的核心和報告系統架構相對簡單。

如果您的要求屬於這兩個類別之一,那麼最好不要將歷史數據存儲在操作系統中。將歷史功能分離到另一個子系統中可能會減少總體工作量,併產生事務性和審計/報告數據庫,這些數據庫對於其預期用途而言效果更好。

+0

我想我明白你在說什麼。所以我對我的FOO_Hist表做了真正的創建一個審計表。我沒有在更新中使用觸發器插入審計表,而是在程序中運行了一條語句。那是對的嗎? – Aaron 2010-10-06 15:57:13

+4

非常。不過,使用觸發器進行這種審計日誌記錄會更好;觸發器確保任何更改(包括手動數據修復)都會記錄在審計日誌中。如果您有超過10-20個表來審計,則可能會更快地構建觸發生成器工具。如果審計日誌的磁盤通信量有問題,則可以將審計日誌表放在單獨的一組磁盤上。 – ConcernedOfTunbridgeWells 2010-10-06 15:59:52

+0

是的,我100%同意這一點。謝謝。 – Aaron 2010-10-06 17:17:55

0

你可以將表格分區嗎?

「使用SQL Server 2008分區表和索引策略當數據庫表增長到幾百GB或更多時,加載新數據,刪除舊數據和維護索引可能變得更加困難。如果表的大小足以導致此類操作花費更長時間,則即使是必須加載或刪除的數據也可能非常大,從而導致表上的INSERT和DELETE操作不切實際。Microsoft SQL Server 2008數據庫軟件提供了表分區操作更易於管理。「

+0

是的,我可以分區表,但是,標準與歷史數據處理時?歷史數據是否應該與活動數據包含在同一個表中?這些是我想討論的問題。這也不是任意的,因爲它涉及到SQL Server 2008. – Aaron 2010-10-06 15:42:28

24

我不認爲有一個特定的標準方式做到這一點,但我想我會拋出一種可能的方法。我在Oracle和我們的內部Web應用程序框架中工作,該框架利用XML來存儲應用程序數據。

我們使用一種叫做主 - 詳細信息模型,它是最簡單的組成:例如稱爲Widgets往往只是包含ID

主表。通常會包含不會隨時間變化的數據/不是歷史數據。

詳細內容/歷史表例如稱爲Widget_Details含有至少:

  • ID - 主鍵。詳細內容/歷史ID
  • MASTER_ID - 例如在這種情況下被稱爲「WIDGET_ID」,這是FK到主記錄
  • START_DATETIME - 時間戳指示數據庫行
  • END_DATETIME的開始 - 時間戳指示的端部該數據庫行
  • STATUS_CONTROL - 單個字符列指示該行的狀態。 'C'表示當前,NULL或'A'將被歷史/存檔。我們只能用這個,因爲我們不能在END_DATETIME指數爲NULL
  • CREATED_BY_WUA_ID - 商店導致該行創建
  • XMLDATA的帳戶的ID - 存儲的實際數據

所以基本上,一個實體首先在主數據庫中有1行,在細節中有1行。具有NULL結束日期和'C'的STATUS_CONTROL的細節。 發生更新時,當前行更新爲當前時間的END_DATETIME,並且status_control設置爲NULL(如果需要,則爲'A')。在詳細信息表中創建了一個新行,該行仍與同一主數據庫相連,其中有status_control'C',進行更新的人員的id以及存儲在XMLDATA列中的新數據。

這是我們歷史模型的基礎。創建/更新邏輯在Oracle PL/SQL包中處理,因此您只需將函數傳遞給當前ID,用戶標識和新XML數據,並在內部執行所有行更新/插入操作以表示歷史模型。開始和結束時間表示表格中該行何時處於活動狀態。

存儲很便宜,我們通常不會刪除數據,而更願意保留審計線索。這使我們可以在任何給定的時間查看我們的數據。通過索引status_control ='C'或使用視圖,混亂並不是一個問題。顯然你的查詢需要考慮到你應該總是使用當前(NULL end_datetime和status_control ='C')版本的記錄。

+0

嗨克里斯,如果你這樣做,身份證(主鍵)必須改變嗎?如果另一個表使用另一個表的關係,那麼它又如何呢? – projo 2013-09-10 00:26:36

+0

@projo你的主表上的ID是PK,在概念上是你處理任何概念的「PK」。詳細信息表上的ID是用於標識主服務器的歷史版本(這是詳細信息的另一列)的PK。在建立關係時,您經常會引用您的概念的真實PK(即主表上的ID或您的詳細信息上的MASTER_ID列),並使用STATUS_CONTROL ='C'來確保您獲得當前版本。或者,您可以引用詳細信息ID以將某些事物與特定時間點相關聯。 – 2013-11-06 14:05:34

+0

+1我已經在幾個大型項目上取得了巨大成功。 – 2014-02-03 12:28:26

7

我覺得你的做法是正確的。歷史表應該是沒有索引的主表副本,請確保您在表中也有更新時間戳。

如果你嘗試其他方法很快你將面對的問題:

  • 維護開銷
  • 多個標誌中選擇
  • 查詢放緩表
  • 增長,指標
0

真正的問題是你需要將歷史數據和活動數據一起用於報告嗎?如果是這樣,請將它們保存在一張表中,然後爲活動記錄中的活動記錄創建一個視圖以供活動查詢使用如果你只需要偶爾看看他們(研究法律問題或其他問題),然後把它們放在一個單獨的表格中。

+1

在幾個歷史報告中「加入」兩張表格更加困難,還是難以修改每個表格插入/更新/刪除以瞭解歷史問題?實際上,審計日誌甚至會包含歷史表中的當前數據,因此在報表中甚至不需要當前表。 – 2013-06-13 14:29:31

-1

另一種選擇是歸檔[每日|每小時|無論]運行數據基礎。大多數數據庫引擎support the extraction of the data into an archive

基本上,這個想法是創建一個計劃Windows或定時任務

  1. 確定在操作數據庫
  2. 當前表從每個表選擇所有數據到CSV或XML文件
  3. 將導出的數據壓縮爲ZIP文件,最好使用文件名中的生成時間戳以便於歸檔。

許多SQL數據庫引擎配備了可用於這一目的的工具。例如,在Linux上使用MySQL時,下面的命令可以在cron作業用來調度提取:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz 
+0

你能解釋一下downvotes的原因嗎? – hamdiakoguz 2016-11-30 17:56:27

+0

這對歷史數據並不合適,因爲如果有人在存檔週期內更改了一個值並將其更改回來,則更新將丟失。也沒有簡單的方法來查看隨着時間的推移對一個實體的更改或部分恢復實體。 – Sgoettschkes 2017-01-19 08:56:55

0

我知道這個老的文章,但只是想補充幾點。 這種問題的標準是最適合這種情況的標準。瞭解這種存儲的需求以及潛在使用歷史/審計/更改跟蹤數據非常重要。

審計(安全目的):對所有可審計表使用公用表。定義結構來存儲列名,值和值之後的字段。

存檔/歷史:像以前跟蹤地址,電話號碼等,創建一個單獨的表FOO_HIST情況下是更好,如果你的活動事務表架構不顯著在未來的(改變,如果你的歷史記錄表必須有相同的結構)。 如果您預計表格規範化,數據類型更改添加/刪除列,請以xml格式存儲您的歷史數據。定義一個包含以下列(ID,日期,架構版本,XMLData)的表格。這將輕鬆處理模式更改。但你必須處理XML,這可能會引起一定程度的數據檢索複雜性。

0

只是想添加一個選項,我開始使用,因爲我使用Azure SQL,而多表的事情對我來說太麻煩了。我在我的表上添加了插入/更新/刪除觸發器,然後使用「FOR JSON AUTO」功能將前/後更改轉換爲json。

SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO) 
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO) 

這會在更改之前/之後返回記錄的JSON表示。然後,我將這些值存儲在歷史記錄表中,其中包含發生更改的時間戳(我也存儲當前關注記錄的ID)。使用序列化過程,我可以控制在模式更改情況下如何回填數據。

我得知這個從這個鏈接here