在企業(yè)數(shù)據(jù)庫設計中,經(jīng)常會遇到一個需求,就是希望把操作之前的數(shù)據(jù)保留下來,能夠看到操作之前是什么數(shù)據(jù),操作之后是什么數(shù)據(jù)。對于這種需求,我們可以使用保留歷史數(shù)據(jù)或者使用版本來實現(xiàn)。
為了能夠保留歷史數(shù)據(jù),在版本設計時有以下方案:
?
一、使用版本號
版本號是一種常見的版本設計方案,就是在要進行歷史數(shù)據(jù)保留的表上面增加一個版本號字段,該字段可以是DateTime類型,也可以是int類型,每進行數(shù)據(jù)操作時,都是創(chuàng)建一個新的版本,版本是只增不減的,所以只需要拿到最大一個版本號,就能得到最新的業(yè)務數(shù)據(jù)。
版 本號除了能夠用于留存歷史數(shù)據(jù)外,還有一個功能就是避免并發(fā)編輯操作。比如我們有一個對象A,當前的版本是1,兩個用戶同時打開了該對象的編輯頁面,進行 數(shù)據(jù)更改。先是甲用戶提交更改,這個時候系統(tǒng)把對象的ID和版本進行查詢,發(fā)現(xiàn)要修改的數(shù)據(jù)最新版本是1,所以成功修改,保存了對象A的新版本2。這個時 候用戶乙也提交了修改。系統(tǒng)把對象的ID和版本1進行查詢,發(fā)現(xiàn)要修改的數(shù)據(jù)最新版本是2,不符合要求,所以拒絕用戶乙的修改。用戶乙只有刷新界面,拿到 最新的版本2,再進行修改。
ID | 單號 | 金額 | 版本號 |
1 | EXP123 | 100 | 1 |
在使用版本號的情況下,對單據(jù)的金額進行修改,修改后創(chuàng)建新的版本號2:
ID | 單號 | 金額 | 版本號 |
1 | EXP123 | 100 | 1 |
2 | EXP123 | 120 | 2 |
二、使用生效、失效時間
保存歷史數(shù)據(jù)的第二辦法是使用生效失效時間來表示一個版本。要進行歷史數(shù)據(jù)記錄的表增加“生效時間”“失效時間”兩個字段,兩個字段不允許為空。對于剛創(chuàng) 建的數(shù)據(jù),生效時間是創(chuàng)建該數(shù)據(jù)的時間,失效時間是9999-12-31。現(xiàn)在對這條數(shù)據(jù)進行了修改,那么我們只需要將當前時間設置為上一個版本的失效時 間,同時創(chuàng)建一條新數(shù)據(jù),生效時間是當前時間,失效時間是9999-12-31即可。
ID | 單號 | 金額 | 生效時間 | 失效時間 |
1 | EXP123 | 100 | 2013/9/1 15:30:00 | 9999/12/31 23:59:59 |
比如上面一條單據(jù),是2013-9-1創(chuàng)建的,后來在2013-9-9 15:00:00對該單據(jù)進行修改,將金額從100修改為120,保存時創(chuàng)建的新數(shù)據(jù)如下:
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 |
使用了生效、失效時間后,我們可以查詢?nèi)我鈺r刻數(shù)據(jù)庫中數(shù)據(jù)的值,只需要把要查詢的時刻傳入,然后between 生效時間 and 失效時間即可。
使用前兩種方案都需要一個業(yè)務主鍵來標識具體的一個業(yè)務數(shù)據(jù)。如果我們要記錄的實體沒有明確的“單號”、“訂單號”這類的業(yè)務主鍵該怎么辦?我們可以使用創(chuàng)建數(shù)據(jù)時的數(shù)據(jù)庫主鍵作為業(yè)務主鍵。
員工ID | 姓名 | 生日 | 業(yè)務ID | 版本號 |
1 | 張三 | 1984/12/29 | 1 | 1 |
比如我們有個員工表,記錄員工基本信息,在創(chuàng)建張三這個員工的數(shù)據(jù)時,其在數(shù)據(jù)庫的ID為1,那么可以將其業(yè)務ID也設置為1。接下來對張三的屬性進行更改,記錄了版本,那么就會創(chuàng)建新的版本,其主鍵“員工ID”會變化,但是其業(yè)務主鍵“業(yè)務ID”始終是1,不會變化的。
員工ID | 姓名 | 生日 | 業(yè)務ID | 版本號 |
1 | 張三 | 1984/12/29 | 1 | 1 |
2 | 張三 | 1985/1/9 | 1 | 2 |
使 用前面兩個方案雖然能夠很好的記錄歷史數(shù)據(jù),但是每次修改數(shù)據(jù)都會導致新版本生成保存,所以每個版本的ID都是新的,所以必須有一個業(yè)務主鍵來標識一個實 體,這里的兩個例子“單號”就是其業(yè)務主鍵。主鍵的變動使得所有關聯(lián)的對象都得變動,從而形成連鎖效應,使得各個關聯(lián)的對象也生成新的版本。比如我們有個 訂單系統(tǒng),里面有訂單表和訂單明細表。現(xiàn)在我們要對訂單的修改記錄歷史版本,所以增加了生效時間和實效時間,并使用訂單號作為業(yè)務主鍵。現(xiàn)在有一個訂單 A,下面有100條明細,如果要對訂單進行修改,將某一條明細的屬性進行修改,從而導致整個訂單的變化,那么我們就需要創(chuàng)建新的訂單數(shù)據(jù)行,由于主鍵變 動,所以訂單明細都需要變動,所以100條明細都需要創(chuàng)建新的版本,新版本的訂單明細中,“訂單ID”指向了新的版本的訂單數(shù)據(jù)的ID。
這樣的設計造成的問題就是訂單明細表會極速膨脹,如果一個訂單有1000條明細,我們只是修改了訂單本身的屬性,并不修改訂單明細,也會造成對這1000條明細做Copy,然后保存。那怎么辦呢?我們可以使用以下辦法:
1.對訂單明細建立版本字段,將版本的粒度細化到訂單明細,而不是訂單。訂單與訂單明細不存在數(shù)據(jù)庫級的外鍵關系,只存在業(yè)務級的外鍵關系。也就是說訂單明細表中增加生效時間、失效時間之外,還需要增加“訂單號”這個字段,用于表名該明細是屬于哪個訂單的。
我 們這么修改后,如果訂單對象進行了修改,訂單明細沒有修改(比如改了一下收件人信息),那么只需要在訂單表中生成新的一行數(shù)據(jù),訂單明細不會Copy生成 新的數(shù)據(jù)。如果我們對某一條訂單明細進行了更改(比調(diào)整了單價、數(shù)量)那么只需要對具體修改的那條訂單明細進行更改,而不需要對整個訂單的所有明細進行更 改。
使用這種設計后,查詢訂單及其明細,需要對兩個表執(zhí)行生效失效時間的過濾,而且明細的獲取是通過訂單號去取,而不是通過訂單ID去取。
將版本控制的粒度細化到訂單明細時,后臺程序的邏輯也會更加復雜。用戶在界面上操作的是訂單對象,系統(tǒng)會將整個修改后的訂單對象傳到后臺,后臺程序需要對每個訂單項進行對比,如果發(fā)現(xiàn)訂單項進行了修改,那么就會調(diào)用生成新版本訂單明細的方法。
2.使用單獨的歷史表
這是另外一種實現(xiàn)歷史版本記錄的方法:
三、使用單獨的歷史表
使 用歷史表其實就是建立完全相同Schema的表(當然,也可以添加更多的字段用于記錄額外的歷史版本信息),該表只保留歷史版本的數(shù)據(jù)。這有點像一個歸檔 邏輯,所有歷史版本我們認為都應該是不經(jīng)常訪問的,所有可以扔到單獨的表,對于現(xiàn)有生效的版本,仍然保留在原表中,如果需要查詢歷史版本,那么就從歷史表 中查詢。
使用單獨的歷史表有以下好處:
- 業(yè)務數(shù)據(jù)表的數(shù)據(jù)量不會因為歷史版本記錄而膨脹。因為歷史數(shù)據(jù)都記錄到了另外一個表中,所以業(yè)務數(shù)據(jù)表只記錄了一份數(shù)據(jù)。
- 業(yè)務數(shù)據(jù)表的Schema不需要調(diào)整,增加額外的版本字段。由于對原有數(shù)據(jù)表不做Schema變更,所以原有查詢邏輯也不用更改。對于一個現(xiàn)有的數(shù)據(jù)庫設計,在增加歷史數(shù)據(jù)記錄功能時更簡單。
- 業(yè)務數(shù)據(jù)表可以直接進行update操作,不會生成新的ID。由于ID不會變,所以我們并需要業(yè)務主鍵應用到程序邏輯中。
使 用歷史表記錄歷史版本主要是要對數(shù)據(jù)操作方法(增加、刪除、修改)進行修改,使得每次數(shù)據(jù)操作時,先在歷史表中留痕,然后再進行數(shù)據(jù)操作。另外就是對查詢 歷史版本功能進行修改,因為歷史數(shù)據(jù)在另外一個表中,所以對于的SQL是不一樣的。當然,我們也可以創(chuàng)建歷史版本數(shù)據(jù)庫,里面保存了所有的歷史表。
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
