2009-12-07 45 views
3

我做了一些項目(CMS和EC系統),需要一些數據版本。數據庫版本號

通常我都用那種模式的

+--------------+ 
+ foobar  + 
+--------------+ 
+ foobar_id + 
+ version  + 
+--------------+ 

它的工作很好,但我想知道如果有一個這樣做的更好。該解決方案的主要問題是您必須始終使用子查詢來獲取最新版本。

即:

SELECT * FROM foobar WHERE foobar_id = 2 and version = (SELECT MAX(version) FROM foobar f2 WHERE f2 = 2) 

這使得大多數查詢更復雜,也有一些性能缺陷。

因此,如果您分享您創建版本化表格的經驗以及每種方法的利弊,那將是非常好的。

謝謝

+0

不是一個數據庫人,但你不能重構該聲明到一個存儲過程? – RCIX 2009-12-07 07:01:46

+0

可能,但我大部分時間使用MySQL – RageZ 2009-12-07 07:52:19

回答

5

我更喜歡在另一個表中有歷史數據。我會做foobar_history或類似的東西,並做一個FK foobar_id。這會阻止你必須一起使用子查詢。這還有一個好處,就是不會用大量的歷史數據來污染你的主數據表,你可能不想看到99%的訪問時間。

雖然您可能希望觸發更新此數據,因爲它需要您將當前數據複製到_history中,然後執行更新。

+0

@jcm:謝謝JCM是的,因爲方法似乎對我來說實際上好多了。我將不得不重做很多代碼,但這是不可能的。謝謝。 – RageZ 2009-12-07 07:00:47

+0

我們這樣做,並使用觸發器將CURRENT記錄存儲到UPDATE和DELETE上的歷史記錄表中 - 在歷史記錄表中增加一列以確定是否更新或刪除以及操作的日期/時間。請注意,我們不會將當前記錄存儲在歷史記錄表中(即,在INSERT中沒有觸發器),我確實看到很多審計系統都這樣做,但是對我來說這是對主表中已有的數據使用大量的磁盤空間。在Main_ID + AuditDate的歷史記錄表上有一個PK對我們來說並不是唯一的,所以您可能需要考慮一個IDENTITY列 – Kristen 2009-12-07 10:58:24

+0

我唯一的疑問是,您有一個VERSION列。如果每個編輯都創建一個新版本,那麼歷史記錄表格就可以了,如果用戶/某個進程定義了什麼時候新版本已經「實現」了,那麼這需要更多的邏輯。您仍然可以查看版本列,歷史記錄中的每個更改,並使用給定版本號查詢最新歷史記錄 - 包括主表以防當前版本的最新副本請求:) – Kristen 2009-12-07 11:00:13

1

您可以通過在表格上使用過濾到最新版本的視圖來簡化查詢。這隻會使查詢看起來更好,你仍然有性能開銷。

2

我認爲最乾淨的解決方案是爲每個需要版本控制的表提供一個歷史記錄表。換句話說,有一個foobar表,然後是一個foobar_History表,在foobar上有一個觸發器,它會將現有數據寫入歷史表,其中包含一個時間戳和更改數據的用戶。較舊的數據很容易查詢,按時間戳降序排序,並且您知道主表中的數據始終是最新版本。

+0

@baldy:是的,這是有道理的,並且實際上使得很多事情更容易,就像我可以使用INSERT INTO SELECT來複制數據一樣,而且我不必在任何地方都有'max'。好吧,讓我們這樣做,我不得不重做很多代碼,但我應該想到之前;-) – RageZ 2009-12-07 06:58:06

0

如果你已經使用了Oracle,你可以使用分析功能

SELECT * FROM( RN 選擇一個。* ,ROW_NUMBER()以上(分區由foobar_id爲了通過版本DESC)FROM foobar的一個 WHERE foobar_id = 2 )其中rn = 1

2

我曾經在一個包含歷史數據的系統上工作,並且我們有一個布爾值來表明哪一個是最新版本的數據。當然,你需要在適用層面保持國旗的穩定。然後,您可以創建使用該標誌的索引,並且如果您在where子句中提供該索引,它就會很快。

臨:

  • 容易理解
  • 不需要你的(現有的)數據庫架構
  • 沒有必要在另一個表複製舊的數據發生重大變化,只有標誌被更新。

缺點:

  • 標誌需要在應用性水平保持

否則,您可以單獨依靠歷史表,在幾個答案建議。

臨:從實際數據的歷史

  • 乾淨的盲分離Blind
  • 可能有實際的數據和歷史之間的分貝級級聯刪除,以防實體被刪除

缺點:

  • 需要2個查詢(或聯合)如果你想完整的歷史(也就是說,舊數據+當前數據)
  • 對應於最新版本數據的行將被更新。我聽說更新比插入更慢,這取決於更改的數據的「大小」。

什麼是最好的取決於你的用例。我不得不處理一個我們想要版本文檔的文檔管理系統。但是我們也有恢復到舊版本的功能。使用布爾值更容易加快只需要最後一個操作的操作。如果你有真實的歷史數據(從不改變),那麼專用的歷史數據表可能會更好。

歷史概念是否適合您的域模型?如果不是,那麼你有一個與你的概念域模型不同的數據庫模式。如果在域級別,實際數據和舊數據需要以同樣的方式處理,則有兩個表會使設計複雜化。只要考慮你需要返回完整歷史(舊+新)的情況。最簡單的解決方案是每個表有一個類,但是不能像列表中那樣只返回一個表。但是,如果這些是兩個截然不同的概念,那麼在您的設計中將歷史記錄放在首位是很好的。

我還建議本文章由M.福勒也很有趣,當談到處理時間數據:Patterns for things that change with time

1

常用的方法是添加一列version_status當前/過期。此外,如果您將新記錄和舊記錄保存在同一個表中,則應該爲您的實體設置一個業務(自然)鍵,如name + pin,因爲主鍵將隨着每行更改(增加)。

TABLE foobar(foobar_id PK, business_key, version, version_status, .....) 

SELECT * 
FROM foobar 
WHERE business_key = 'myFoobar3' AND version_status = 'current' 

在決定要保留歷史記錄在同一個表 - 或將其移動到一個單獨的一個 - 滿意,因爲他們foobar_id作爲外鍵的其他表。在發佈新版本時,現有的外鍵是指向新的PK還是舊的PK?如果你想保持關係的歷史,你可能想要把所有東西放在同一張表中。如果只有新版本很重要,那麼可以考慮將過期的行移到另一個表中 - 儘管這不是必需的。

0

這取決於有多少表需要版本控制,以及是否有事務性礦石報告系統。

如果只是一些事務性表格 - 只要性能問題不太重要,那麼您所採取的方式就沒有問題。您可以通過爲current_row添加一列和使先前行更新爲非當前的觸發器來更輕鬆地查詢。

但是,如果你有很多表或額外的行正在減慢你的一些查詢,那麼我會照其他人的建議來使用歷史表以及歷史觸發器。請注意,您可以生成該代碼,以便更易於開發維護。

如果你在報告世界,那麼有很多其他選項我不會在這裏解決。您可以在數據倉庫數據建模書中找到詳細的選項。