關於數據庫‘狀態’字段設計的思考與實踐

最近在作訂單及支付相關的系統,在訂單表的設計階段,團隊成員就‘訂單狀態’數據庫字段設計有了一些分歧,網上也有很多關於這方面的思考和探討,結合這些資料和項目的實際狀況,擬對一些共性問題進行更深一層的思考,筆耕在此,和你們一塊兒探討。html

問題綜述

這裏的分歧點即有團隊內部的分歧點,也有網絡上常見的一些分歧點,先將存在的分歧點拋出來:sql

一、訂單表的‘訂單狀態’字段對應的字典值應當包含哪些狀態值?對於‘已評論’、‘已退貨’、’已退款’這類狀態是放到‘訂單狀態’中?仍是獨立一個字段標識?數據庫

二、訂單表的‘訂單狀態’字段對應的字典值如何表示?可選項有:使用數字標識、使用多‘位’存儲方式標識、使用具備明確業務含義的英文字符串標識;網絡

三、訂單表的‘訂單狀態’字段使用何種類型?可選項有:number(N)、char(N)、varchar2(N);工具

若是嫌分析過程過於囉嗦,能夠直接拉到最後看結論。性能

業務分析

咱們先不去看問題,先來看看和‘訂單(Order)’實體相關的業務是怎樣的。下面咱們會針對可能改變訂單實體狀態的行爲已經狀態變化的可能性進行詳細的分析。ui

訂單業務實體相關的業務流程以下:下單(create)--> 買家付款(pay)--> 賣家發貨(deliver)-->買家收貨(receive)-->退貨(rereturn);此外,還有退款(refund)和評論(comment),這兩個行爲比較特殊,其前向行爲可能存在多個。編碼

首先,能夠改變訂單業務狀態【這裏的狀態不是指‘訂單狀態’(OrderState)這個數據庫字段,而是指實際業務狀態,咱們簡記爲(BizState),以和OrderState區分開】的行爲有哪些?按照典型電商的業務流程,主要的行爲(action)有:下單、付款、發貨、收貨、退款/退貨、評論;每一種行爲的發生,都會致使訂單的業務狀態BizState發生變化,好比‘下單’行爲會建立訂單,‘付款’行爲會使訂單變爲‘已付款’,‘發貨’行爲可使訂單狀態變爲‘已發貨’,‘收貨’行爲會使訂單狀態變爲‘已收貨’,‘評論’行爲會使訂單狀態變爲‘已評論’。‘退款/退貨’action不是全部訂單都支持的,爲減少複雜度,暫不考慮它們。spa

其次,細分下每種action對BizState帶來的影響,會發現還能夠細分爲四種子狀態(subState):action未開始(標記爲0)、action進行中(標記爲1)、action成功(標記爲2)、action失敗(標記爲3);理論上,將全部action的全部subState進行排列獲得4*4*4*4*4=1024(暫未考慮‘退貨’);實際上,不少組合是沒有業務意義的,是不可能存在的,好比‘未開始已付款...’(***20)這一類組合是不可能發生的,應當捨棄。用表格將上述的組合分析以下:.net

經過上表,咱們能夠發現些的規律:

‘下單’、‘付款’、‘發貨’、‘收貨’前四種action是存在依賴關係的,亦即後一個action依賴於前一個action的完成;因此,他們的SubState組合狀況就會很是少;

‘評論comment’這個action的SubState和其餘狀態組合會有不少種可能性;除了前面了兩行是‘X’,後面是‘?’或者‘Y’,‘?’是指需求上是否容許在對應的BizState上進行評論,若是容許,則每種BizState須要多出4種可能,這樣組合的可能性就會變得很大。

沒有業務意義的SubState組合被捨棄。表中的標黑單元格,表示這個BizState是毫無心義的,由於‘未下單’的訂單對於咱們來說是不存在的,這類組合須要捨棄;一樣的,還有不少其餘的組合也是不存在的,被捨棄掉,未展現在上表中,如‘已下單已付款未發貨已收貨’這種。

一般某個action的SubState爲‘1進行中’、‘3失敗’時,會被忽略,但也有例外;好比‘付款’action的‘3失敗’狀態,和‘付款’action的‘1進行中’狀態,具體分析見後面內容。

忽略全部action的‘0未開始’SubState狀態。由於這類SubState對於BizState不會帶來變化。

綜合下來,咱們獲得上表的BizState,注意這裏的Comment action未進行細化處理,若是細化處理,會發現BizState的可能性會增大不少不少。

接下來咱們就以前提出的這些問題進行逐個討論。

問題1、訂單表的‘訂單狀態’字段應當包含哪些狀態值?

什麼樣的‘訂單業務狀態’(BizState)須要記錄到系統層面的‘訂單狀態’(OrderState)字段呢?若是記錄多了,則系統處理的複雜度會增大;記錄少了,那麼‘訂單狀態’(OrderState)字段就不能完整的表示出訂單實體狀態變化狀況。

核心狀態

經過上面的業務分析可知:大部分存在依賴關係的action(create、pay、deliver、receive),他們產生的合理的SubState組合是很是少的,並且他們之間的依賴是單向依賴,狀態機的處理也很簡單,所以,咱們先將這部分BizState歸入到OrderState中:

  • 等待買家付款
  • 買家付款成功
  • 賣家已發貨
  • 買家已收貨

目前的訂單狀態流轉:

‘action行爲’失敗的狀況

對於action的SubState是‘3失敗’的處理,須要針對不一樣的action進行分析。相似‘下單Create’這樣的action,若是失敗,則能夠直接將OrderState置爲‘訂單建立失敗’,由於Create action是第一個action,它的失敗意味着Order實體出生即死,BizState置爲終態,對於這個BizState應當歸入到OrderState中記錄,不過這個OrderState其實對於用戶並沒有多大用處,由於用戶並不會關心下單失敗的訂單,他更關心的是從新下單;

對於‘支付’失敗,則要看需求,若是需求要求用戶能夠繼續支付,則訂單須要保留,而且狀態仍然爲‘等待買家付款’,若是不容許再支付,則理論上能夠將BizState置爲‘支付失敗’終態,因此,‘支付失敗’的BizState終態也應當記錄到OrderState字段中。

對於‘發貨’失敗、‘收貨’失敗的狀況,一般是不會發生的,即便發生也不屬於系統可以控制的範疇,系統記錄並沒有意義,更具建設性的作法是經過線下手段儘快解決問題,從新發貨等等,因此對於這些狀態系統的OrderState字段不予記錄。

這樣下來咱們的OrderState字典值增長到6個,加粗項爲新增:

  • 建立訂單失敗(終態)
  • 等待買家付款
  • 買家付款失敗(終態,依賴需求而定)
  • 買家付款成功
  • 賣家已發貨
  • 買家已收貨

目前的訂單狀態流轉:

‘action行爲’進行中的狀況

對於action的SubState是‘1進行中’的處理,一樣須要具體場景具體分析。‘付款’行爲是用戶發起的,可是並非和訂單系統之間的交互,涉及到支付系統的處理,這個領域也不是訂單系統可控的,但關係到錢,用戶比較關係,因此對於這樣一箇中間態,咱們須要記錄,以便用戶經過訂單系統查詢訂單狀態,爲便於用戶理解,將此狀態在OrderState中記爲‘付款確認中’;‘發貨’‘收貨’進行中的狀況,不是訂單系統能夠控制的領域,咱們能夠把他們當着行爲‘未開始’處理,好比‘發貨進行中’,訂單系統的OrderState值爲‘買家已付款’,但給用戶看到的提示信息是‘買家已付款,等待賣家發貨’,實際上這時候賣家可能正在發貨中,可是用戶不會去關心到底有沒有打包好貨物什麼的,因此這類‘進行中’狀態能夠捨棄。這樣下來訂單系統的OrderState字段又多了一個字典值:‘付款確認中’:

  • 建立訂單失敗(終態)
  • 等待買家付款
  • 付款確認中
  • 買家付款失敗(終態,依賴需求而定)
  • 買家付款成功
  • 賣家已發貨
  • 買家已收貨

目前的訂單狀態流轉:

‘action行爲’未開始的狀況

忽略全部action的‘0未開始’SubState狀態。由於這類SubState對於BizState不會帶來變化。

‘評論comment’的處理

最後,再來看看‘評論comment’這個action。若是需求上要求:只有買家收貨後才能發起‘評論’操做,則能夠任務‘評論comment’單向依賴於‘receive收貨’行爲,那麼能夠將這個action的subState對應的少許BizState(應當只有‘買家已評論’、‘賣家已評論’狀態)歸入OrderState字段統一記錄;可是若是需求是:買家在下單後就能夠開始評論,好比若是賣家發貨慢了,買家能夠上去吐槽,那麼‘評論comment’就不是單向依賴於‘receive收貨’行爲了,而是多向依賴於‘pay付款’、‘deliver發貨’、‘receive收貨’,那麼這些actions的subState組合可能性就暴增,BizState的字典取值也會暴增,顯然,不該當將這麼多的BizState交給OrderState來記錄,而應當由一個獨立的數據庫字段負責記錄‘評論comment’的SubState,咱們能夠將這個字段取名爲‘CommentState’(評論狀態),它的字典值很少,只有:‘未評論’、‘買家已評論’、‘賣家已評論’;其實,對於前一種需求,也能夠不講‘評論comment’對應的SubState產生的BizState歸入OrderState,由於用戶對於評論與否其實並非那麼關心的,也就是說‘評論comment’並非核心業務流程,爲了下降核心業務流程的系統處理複雜度,將其從核心業務流程中剝離出來較好。

綜上,咱們應當將‘評論comment’對應的BizState獨立到一個字段中記錄。

‘退貨rereturn’的處理

再來看看‘退貨rereturn’行爲對應的BizState的處理。‘退貨rereturn’並非全部訂單都會經歷的,可是一旦涉及,則‘退貨return’在業務流程上一定是單向依賴於‘receive收貨’,因此應當將‘退貨return’產生的BizState(‘退貨中’、‘退貨成功’,‘退款失敗’和‘未退貨’被忽略,見上面解釋)歸入OrderState一併記錄;這樣咱們的OrderState有多了兩種字典值,這裏咱們不考慮一個訂單中有多種商品的狀況,故把‘退貨成功'當着終態處理,若是是一個訂單多種貨物的狀況,須要從新仔細分析。加粗項爲新增:

  • 建立訂單失敗(終態)
  • 等待買家付款
  • 付款確認中
  • 買家付款失敗(終態,依賴需求而定)
  • 買家付款成功
  • 賣家已發貨
  • 買家已收貨
  • 退貨中
  • 退貨成功(終態)

目前的訂單狀態流轉:

‘退款refund’的處理

最後來看下‘退款refund’行爲對應的BizState的處理。首先,咱們須要知道‘退貨’和‘退款’是兩種不一樣的業務行爲,他們的關係是:一般意義上,‘退貨’必然致使‘退款’,可是‘退款’能夠沒有‘退貨’的參與(這裏不討論特殊狀況,好比對於虛擬貨物來說,付款成功一般覺得着收貨成功,這時候就只能是在由‘退貨’致使‘退款’),好比電商容許用戶付款成功後收到貨物前發起‘退款’。也就是說‘退款refund’並不單向依賴於‘退貨rereturn’,和‘評論comment’同樣是多項依賴,因此,咱們能夠參考‘評論comment’的處理方式,單獨創建一個字段‘RefundState退款狀態’記錄‘退款refund’產生的BizState,這個狀態字段的字典值有:退款中,退款成功。

其餘狀況考慮

另外,可能還有一些加強型需求,讓客戶體驗更好,好比用戶能夠建立訂單以後付款以前,將訂單取消,或者由系統跑批將用戶長時間未支付的訂單關閉,這會產生一種新的action——‘close關閉’,對應的會產生一種新的有意義的BizState——‘訂單關閉/取消’,這個不屬於核心流程中的,且並沒有糾結之處,不予詳細討論,羅列以下:

  • 建立訂單失敗(終態)
  • 等待買家付款
  • 付款確認中
  • 買家付款失敗(終態,依賴需求而定)
  • 買家付款成功
  • 賣家已發貨
  • 買家已收貨
  • 退貨中
  • 退貨成功(終態)
  • 訂單關閉(終態)

結論

綜上,咱們能夠得出放入數據庫’訂單狀態‘字段的標準:核心業務流程,向前單向依賴。擴展到其餘業務實體是同樣的,這裏說的’訂單狀態‘字段實際是指該業務實體對應的數據表的主業務狀態字段。咱們把結論擴展一下:

若是某個action屬於業務實體對應的核心業務流程,且該action單向依賴於其前向的action,則須要將這個action產生的BizState放入到業務實體對應的數據庫表的主狀態字段中記錄。

OrderState字段記錄的BizState業務狀態有10種,其中4種是終態,其他狀態爲中間態。這些狀態的流轉關係爲:

問題2、訂單表的‘訂單狀態’字段的字典值的表示形式?

先列出可選項:使用數字標識、使用多‘位’存儲方式標識、使用具備明確業務含義的英文字符串標識;對可選項作逐一解釋:

a、使用數字標識——使用一個數字標識一種狀態,並未要求是sequence的;如‘等待買家付款’表示爲‘0’;

b、使用多‘位’存儲方式標識——將某種行爲是否發生對應的狀態對應到一個位上,好比‘是否付款’定義在第一位,‘是否發貨’定義在第二位,‘是否收貨’定義在第三位,‘是否評論’定義在第四位,則狀態‘賣家已收貨未評論’能夠表示爲:0111;而‘等待買家付款’則表示爲‘0000’;固然這裏的‘位’多是二進制的也多是N進制,後面咱們詳細討論。

c、使用具備明確業務含義的英文字符串標識——該方案和方案a相似,不過字典值變爲具備明確業務含義的英文支付串,如‘等待買家付款’表示爲‘WAIT_BUYER_PAY’;

方案a是數據庫字段字典的慣用方式,簡單直觀,可是有一個壞處在於:當字典值較多時,數據庫表的使用者記不住字典的含義,須要反覆查找資料確認;有人會說將字典值寫到字段的註釋裏,這個在實踐中不是很靠譜,一般表創建後,若是字段增長了字典值,一般開發人員都會忽略更改字典值;並且在使用工具(如pl/sql)查詢數據庫時,並不會將全部字典值展現出來;

經過問題一的分析,可知:方案b使用多‘位’存儲方式會增長複雜度,並無必要,能夠經過將‘是否評論’狀態獨立成一個字段進行表示。

方案c和方案a相似,好處在於經過字典值直接知道業務含義,壞處在於會給編碼和手工查詢時帶來複雜度,一般人們也記不住‘等待買家付款’的英文字典是‘WAIT_BUYER_PAY’,那麼手動寫sql查詢‘等待買家付款’時就犯迷糊了。

折中以後,咱們組合方案a和方案c,獲得方案d:另外創建一張字典表,存儲:數字形式的字典值、字典英文名稱、字典中文簡稱、字典解釋;訂單實體表的OrderState字段使用數字做爲字典值。

對於方案d,看到OrderState的數字形式狀態時,能夠先看看字段註釋是否有此字典的定義,若是沒有就取查下字典表,獲得字典值和含義;在編碼和手動sql查詢時也會變得比較容易,數字的位數畢竟要少些;創建字典表的其餘好處還有:字典的解釋能夠寫的很詳細,在報表中要求展現字典中文名時,也能直接從數據庫聯表查詢獲得,而沒必要額外作一次映射。(有參考:數據庫表設計(狀態字段)

那麼對於字典數量不多的狀態字段是否有必要額外新建一張字典表呢?這個根據實際狀況考慮,一般能夠先不建,若是後續有業務場景須要再行建立也不遲。

而對於非業務實體表的系統日誌/跑批記錄表等的狀態,則徹底可使用數字形式的字典,由於一般不會有業務場景使用到這些字典值,並且這些字典值域應當會比較小,因此沒有必要爲他們建立單獨的字典表。

綜上得出結論:

一、字典值域較多、變化較多、報表等業務場景會使用到的業務實體表的業務狀態字段,使用‘方案d:新建字典表’的方案處理;如‘訂單業務實體表’中的‘訂單狀態’字段。

二、字典值域較少、變化較少等業務場景不會使用到的業務實體表的業務狀態字段,使用‘方案a:使用數字標識字典’的方案處理;如‘支付寶的支付流水錶’的‘支付流水狀態’字段。

三、系統日誌/跑批記錄表的狀態字段,使用‘方案a:使用數字標識字典’的方案處理;如‘待收貨記錄表’的‘跑批狀態’字段。

問題3、數據庫表的‘狀態’字段使用何種類型

列出可選項:number(N)、char(N)、varchar2(N),其中N是一個長度值。

這個問題主要須要考慮使用場景、擴展性、性能、存儲。

‘狀態’字段主要使用在查詢場景,且一般是‘=’或者‘in’的查詢,並無區間類的查詢,故三者差異不大;

對於性能,參考[原創]在Oracle 10g,Number、Char和Varchar2類型做爲主鍵,查詢效率分析 char(N)、varchar2(N)性能優於number(N),故舍棄number(N)。

考慮到擴展性,char(N)、varchar2(N)差很少;

考慮到存儲,varchar2更加佔用空間更小,故選擇varchar2(N)。

綜上:選擇varchar2(N)做爲數據庫‘狀態’字段的類型。

問題結論彙總

一、訂單表的‘訂單狀態’字段對應的字典值應當包含哪些狀態值?對於‘已評論’、‘已退貨’這類狀態是放到‘訂單狀態’中?仍是獨立一個字段標識?

若是某個action(行爲,如支付)屬於業務實體對應的核心業務流程,且該action單向依賴於其前向的action,則須要將這個action產生的業務狀態放入到業務實體對應的數據庫表的主狀態字段中記錄。

問題中的‘已評論’由‘評論’行爲產生,而‘評論’這個action並非訂單業務實體的核心業務流程,且可能存在多個前向依賴action(支付、發貨、收貨等),因此應當獨立到一個字段標識。

問題中的‘已退貨’由‘退貨’行爲產生,而‘退貨’這個action是訂單業務實體的核心業務流程,用戶很是關心,且只單向依賴於‘收貨’action,因此應當記錄到訂單業務實體表的‘訂單狀態’字段中。

問題中的‘已退款’由‘退款’行爲產生,而‘退款’這個action是訂單業務實體的核心業務流程,用戶很是關心,可是這個action存在多個前向依賴action(支付、發貨、收貨等),因此應當獨立到一個字段標識。

二、訂單表的‘訂單狀態’字段對應的字典值如何表示?可選項有:使用數字標識、使用多‘位’存儲方式標識、使用具備明確業務含義的英文字符串標識;

i、字典值域較多、變化較多、報表等業務場景會使用到的業務實體表的業務狀態字段,使用‘方案d:新建字典表’的方案處理;如‘訂單業務實體表’中的‘訂單狀態’字段。

j、字典值域較少、變化較少等業務場景使用到的業務實體表的業務狀態字段,使用‘方案a:使用數字標識字典’的方案處理;如‘支付寶的支付流水錶’的‘支付流水狀態’字段。

k、系統日誌/跑批記錄表的狀態字段,使用‘方案a:使用數字標識字典’的方案處理;如‘待收貨記錄表’的‘跑批狀態’字段。

三、訂單表的‘訂單狀態’字段使用何種類型?可選項有:number(N)、char(N)、varchar2(N);

varchar2(N)佔用存儲更少,且具備同等的性能、擴展性,選擇varchar2(N)做爲數據庫‘狀態’字段的類型。

參考資料

數據庫表設計(狀態字段)

[原創]在Oracle 10g,Number、Char和Varchar2類型做爲主鍵,查詢效率分析

相關文章
相關標籤/搜索