2008-11-27 69 views
122

假設我在數據庫中有記錄,並且管理員和普通用戶都可以執行更新。如何版本控制數據庫中的記錄

任何人都可以提出一個很好的方法/架構如何進行版本控制在此表中的每一個變化,從而有可能回退記錄到以前的版本。

回答

123

比方說,您有一個FOO表,管理員和用戶可以更新。大多數情況下,您可以針對FOO表編寫查詢。快樂的時光。

然後,我將創建一個FOO_HISTORY表。它包含FOO表的所有列。主鍵與FOO加上RevisionNumber列相同。有一個從FOO_HISTORYFOO的外鍵。您也可以添加與修訂相關的列,例如UserId和RevisionDate。在所有*_HISTORY表中(即從Oracle序列或等同物中)以不斷增加的方式填充RevisionNumbers。不要只依賴於一秒鐘的變化。即。不要把RevisionDate放入主鍵。

現在,每次更新FOO時,就在更新之前,將舊值插入FOO_HISTORY。你在設計的某個基本層次上這樣做,以便程序員不會意外地錯過這一步。

如果你想從FOO刪除一行,你有一些選擇。可以級聯和刪除所有歷史記錄,也可以通過將FOO標記爲已刪除來執行邏輯刪除。

如果您對當前值非常感興趣並且只是偶爾在歷史記錄中感興趣,則此解決方案非常有用。如果您始終需要歷史記錄,那麼您可以輸入有效的開始和結束日期,並將所有記錄保存在FOO中。每個查詢都需要檢查這些日期。

35

我認爲你正在尋找版本的數據庫中記錄的內容(如StackOverflow上做當有人編輯提問/回答)。一個好的起點可能是看一些使用修訂版跟蹤的數據庫模型。

想到最好的例子是維基百科引擎MediaWiki。比較數據庫圖here,特別是revision table

根據你使用的是什麼技術,你必須找到一些很好的差異/合併算法。

檢查this question是否適用於.NET。

21

在BI世界裏,你可以通過添加的startDate和結束日期到要版本表做到這一點。將第一條記錄插入表中時,會填充startDate,但endDate爲空。當您插入第二條記錄時,還會使用第二條記錄的startDate更新第一條記錄的endDate。

當您要查看當前的記錄,你選擇一個地方結束日期爲空。

這有時被稱爲2型Slowly Changing Dimension。 另請參閱TupleVersioning

+0

使用這種方法,我的桌子不會變得很大嗎? – 2008-11-27 07:32:09

+1

是的,但您可以通過對錶格進行索引和/或分區來處理該問題。而且,只有少量的大桌子。大多數將會小得多。 – ConcernedOfTunbridgeWells 2008-11-27 12:46:26

3

你不說什麼數據庫,我沒有看到它在後標籤。如果是Oracle的話,我可以推薦在Designer中內置的方法:使用journal tables。如果是用於任何其他數據庫,那麼我基本上也推薦同樣的方法...

它的工作方式,如果你想在另一個數據庫中複製它,或者如果你只是想了解它,對於一個表來說,還有一個影子表,它只是一個普通的數據庫表,具有相同的字段規格,以及一些額外的字段:如上次採取的操作(字符串,用於插入的典型值「INS」,「UPD」用於更新,「DEL」用於刪除),操作發生時的日期時間和用戶ID。

通過觸發器,對錶中任何行的操作都會在日誌表中插入一個新行,其中包含新值,採取何種操作,何時以及由什麼用戶執行。你永遠不會刪除任何行(至少不是在過去的幾個月)。是的,它會變得很大,很容易數百萬行的,但你可以很容易地跟蹤任何紀錄時間任何點以來的日誌開始或舊雜誌排最後得到了淨化,誰最後一次修改的值。

在你需要的是爲SQL代碼自動生成,所有你需要做的就是編譯/運行甲骨文的一切;它附帶了一個基本的CRUD應用程序(實際上只有「R」)來檢查它。

3

兩個選項:

  1. 有一個歷史表 - 插入新的數據到這個歷史表只要原來的更新。
  2. 審計表 - 存儲值之前和之後的值 - 僅用於審計表中的修改列以及其他信息,如誰更新和何時更新。
2

您可以通過SQL觸發器在SQL表進行審覈。從觸發器可以訪問2個特殊表格(inserted and deleted)。這些表格包含每次更新表格時插入或刪除的確切行。在觸發器SQL中,您可以將這些修改後的行插入到審計表中。這種方法意味着您的審計對程序員來說是透明的;不需要他們的努力或任何實施性知識。

這種方法的好處是,審計將無論通過數據訪問的DLL發生的SQL操作發生,或通過手動的SQL查詢; (因爲審計是在服務器本身上執行的)。

7

升級到SQL 2008

嘗試使用SQL變更跟蹤,在SQL 2008年而不是時間戳和墓碑列的黑客,你可以用在你的數據庫中的數據跟蹤更改這一新功能。

MSDN SQL 2008 Change Tracking

2

我也是做同樣的事情。我正在爲課程計劃製作一個數據庫。這些計劃需要原子更改版本靈活性。換句話說,無論教學計劃多麼小,每個變化都需要被允許,但舊版本也需要保持不變。這樣,課程創建者可以在學生使用課程時編輯課程計劃。

它的工作方式是,一旦有學生做了教訓,他們的結果連接到他們完成的版本。如果進行更改,他們的結果將始終指向他們的版本。

這樣,如果一節課的標準被刪除或移動,其結果不會改變。

我目前這樣做的方式是處理一個表中的所有數據。通常我只有一個id字段,但是在這個系統中,我使用了一個id和一個sub_id。 sub_id始終保留在行中,通過更新和刪除。該id是自動遞增的。課程計劃軟件將鏈接到最新的sub_id。學生成績將鏈接到ID。我還包含一個時間戳記用於跟蹤發生更改的時間,但沒有必要處理版本控制。

我可能會改變一件事,一旦我測試了它,我可能會使用前面提到的endDate null的想法。在我的系統中,爲了找到最新版本,我必須找到max(id)。其他系統只是查找endDate = null。不知道是否有優惠outway有另一個日期字段。

我的兩分錢。

2

@WW。答案是一個很好的答案另一種方法是製作一個版本列,並將所有版本保存在同一個表中。

爲一個表的方法您:

  • 使用標誌指示最新ALA Word Press
  • 或做比outer join版本討厭更大。

outer join使用方法的版本號的一個例子SQL是:

SELECT tc.* 
FROM text_content tc 
LEFT OUTER JOIN text_content mc ON tc.path = mc.path 
AND mc.revision > tc.revision 
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- path in this case is our natural id. 

壞消息是上述需要outer join和外連接可能會很慢。好消息是創建新條目在理論上更便宜,因爲您可以在中執行一次寫入操作,但不支持事務(假設您的數據庫是原子的)。

一個例子做一個新的修訂版'/stuff'可能是:

INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time) 
(
SELECT 
(md5(random()::text)) -- {id} 
, tc.path 
, 'NEW' -- {data} 
, (tc.revision + 1) 
, 'UPDATE' -- {comment} 
, 't' -- {enabled} 
, tc.create_time 
, now() 
FROM text_content tc 
LEFT OUTER JOIN text_content mc ON tc.path = mc.path 
AND mc.revision > tc.revision 
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- {path} 
) 

我們插入使用舊數據。如果說你只想更新一列並避免樂觀鎖定和/或事務,這是特別有用的。

標記方法和歷史表方法需要插入/更新兩行行。

修訂版編號方法的另一個優點是您可以隨後使用觸發器重構多表格方法,因爲觸發器本質上應該執行上述操作。

5

只是想補充一點,這個問題的一個很好的解決方案是使用Temporal database。許多數據庫供應商開箱即用或通過擴展提供此功能。我已經成功使用PostgreSQL的temporal table擴展,但其他人也有。無論何時更新數據庫中的記錄,數據庫都會保留該記錄的先前版本。