數據庫模型設計——歷史與版本設計

在企業數據庫設計中,常常會遇到一個需求,就是但願把操做以前的數據保留下來,可以看到操做以前是什麼數據,操做以後是什麼數據。對於這種需求,咱們可使用保留歷史數據或者使用版原本實現。數據庫

爲了可以保留歷史數據,在版本設計時有如下方案:併發

 

1、使用版本號

版本號是一種常見的版本設計方案,就是在要進行歷史數據保留的表上面增長一個版本號字段,該字段能夠是DateTime類型,也能夠是int類型,每進行數據操做時,都是建立一個新的版本,版本是隻增不減的,因此只須要拿到最大一個版本號,就能獲得最新的業務數據。數據庫設計

版本號除了可以用於留存歷史數據外,還有一個功能就是避免併發編輯操做。好比咱們有一個對象A,當前的版本是1,兩個用戶同時打開了該對象的編輯頁面,進行數據更改。先是甲用戶提交更改,這個時候系統把對象的ID和版本進行查詢,發現要修改的數據最新版本是1,因此成功修改,保存了對象A的新版本2。這個時候用戶乙也提交了修改。系統把對象的ID和版本1進行查詢,發現要修改的數據最新版本是2,不符合要求,因此拒絕用戶乙的修改。用戶乙只有刷新界面,拿到最新的版本2,再進行修改。spa

ID 單號 金額 版本號
1 EXP123 100 1

在使用版本號的狀況下,對單據的金額進行修改,修改後建立新的版本號2:設計

ID 單號 金額 版本號
1 EXP123 100 1
2 EXP123 120 2

2、使用生效、失效時間

保存歷史數據的第二辦法是使用生效失效時間來表示一個版本。要進行歷史數據記錄的表增長「生效時間」「失效時間」兩個字段,兩個字段不容許爲空。對於剛建立的數據,生效時間是建立該數據的時間,失效時間是9999-12-31。如今對這條數據進行了修改,那麼咱們只須要將當前時間設置爲上一個版本的失效時間,同時建立一條新數據,生效時間是當前時間,失效時間是9999-12-31便可。3d

ID 單號 金額 生效時間 失效時間
1 EXP123 100 2013/9/1 15:30:00 9999/12/31 23:59:59

好比上面一條單據,是2013-9-1建立的,後來在2013-9-9 15:00:00對該單據進行修改,將金額從100修改成120,保存時建立的新數據以下:版本控制

ID 單號 金額 生效時間 失效時間
1 EXP123 100 2013/9/1 15:30:00 2013/9/9 15:00:00
2 EXP123 120 2013/9/9 15:00:00 9999/12/31 23:59:59

使用了生效、失效時間後,咱們能夠查詢任意時刻數據庫中數據的值,只須要把要查詢的時刻傳入,而後between 生效時間 and 失效時間便可。對象

使用前兩種方案都須要一個業務主鍵來標識具體的一個業務數據。若是咱們要記錄的實體沒有明確的「單號」、「訂單號」這類的業務主鍵該怎麼辦?咱們可使用建立數據時的數據庫主鍵做爲業務主鍵。blog

員工ID 姓名 生日 業務ID 版本號
1 張三 1984/12/29 1 1

好比咱們有個員工表,記錄員工基本信息,在建立張三這個員工的數據時,其在數據庫的ID爲1,那麼能夠將其業務ID也設置爲1。接下來對張三的屬性進行更改,記錄了版本,那麼就會建立新的版本,其主鍵「員工ID」會變化,可是其業務主鍵「業務ID」始終是1,不會變化的。ci

員工ID 姓名 生日 業務ID 版本號
1 張三 1984/12/29 1 1
2 張三 1985/1/9 1 2

使用前面兩個方案雖然可以很好的記錄歷史數據,可是每次修改數據都會致使新版本生成保存,因此每一個版本的ID都是新的,因此必須有一個業務主鍵來標識一個實體,這裏的兩個例子「單號」就是其業務主鍵。主鍵的變更使得全部關聯的對象都得變更,從而造成連鎖效應,使得各個關聯的對象也生成新的版本。好比咱們有個訂單系統,裏面有訂單表和訂單明細表。如今咱們要對訂單的修改記錄歷史版本,因此增長了生效時間和實效時間,並使用訂單號做爲業務主鍵。如今有一個訂單A,下面有100條明細,若是要對訂單進行修改,將某一條明細的屬性進行修改,從而致使整個訂單的變化,那麼咱們就須要建立新的訂單數據行,因爲主鍵變更,因此訂單明細都須要變更,因此100條明細都須要建立新的版本,新版本的訂單明細中,「訂單ID」指向了新的版本的訂單數據的ID。

image

這樣的設計形成的問題就是訂單明細表會極速膨脹,若是一個訂單有1000條明細,咱們只是修改了訂單自己的屬性,並不修改訂單明細,也會形成對這1000條明細作Copy,而後保存。那怎麼辦呢?咱們可使用如下辦法:

1.對訂單明細創建版本字段,將版本的粒度細化到訂單明細,而不是訂單。訂單與訂單明細不存在數據庫級的外鍵關係,只存在業務級的外鍵關係。也就是說訂單明細表中增長生效時間、失效時間以外,還須要增長「訂單號」這個字段,用於表名該明細是屬於哪一個訂單的。

image

咱們這麼修改後,若是訂單對象進行了修改,訂單明細沒有修改(好比改了一下收件人信息),那麼只須要在訂單表中生成新的一行數據,訂單明細不會Copy生成新的數據。若是咱們對某一條訂單明細進行了更改(比調整了單價、數量)那麼只須要對具體修改的那條訂單明細進行更改,而不須要對整個訂單的全部明細進行更改。

使用這種設計後,查詢訂單及其明細,須要對兩個表執行生效失效時間的過濾,並且明細的獲取是經過訂單號去取,而不是經過訂單ID去取。

將版本控制的粒度細化到訂單明細時,後臺程序的邏輯也會更加複雜。用戶在界面上操做的是訂單對象,系統會將整個修改後的訂單對象傳到後臺,後臺程序須要對每一個訂單項進行對比,若是發現訂單項進行了修改,那麼就會調用生成新版本訂單明細的方法。

2.使用單獨的歷史表

這是另一種實現歷史版本記錄的方法:

3、使用單獨的歷史表

使用歷史表其實就是創建徹底相同Schema的表(固然,也能夠添加更多的字段用於記錄額外的歷史版本信息),該表只保留歷史版本的數據。這有點像一個歸檔邏輯,全部歷史版本咱們認爲都應該是不常常訪問的,全部能夠扔到單獨的表,對於現有生效的版本,仍然保留在原表中,若是須要查詢歷史版本,那麼就從歷史表中查詢。

使用單獨的歷史表有如下好處:

  • 業務數據表的數據量不會由於歷史版本記錄而膨脹。由於歷史數據都記錄到了另一個表中,因此業務數據表只記錄了一份數據。
  • 業務數據表的Schema不須要調整,增長額外的版本字段。因爲對原有數據表不作Schema變動,因此原有查詢邏輯也不用更改。對於一個現有的數據庫設計,在增長曆史數據記錄功能時更簡單。
  • 業務數據表能夠直接進行update操做,不會生成新的ID。因爲ID不會變,因此咱們並須要業務主鍵應用到程序邏輯中。

使用歷史表記錄歷史版本主要是要對數據操做方法(增長、刪除、修改)進行修改,使得每次數據操做時,先在歷史表中留痕,而後再進行數據操做。另外就是對查詢歷史版本功能進行修改,由於歷史數據在另一個表中,因此對於的SQL是不同的。固然,咱們也能夠建立歷史版本數據庫,裏面保存了全部的歷史表。

相關文章
相關標籤/搜索