天天看點

系統設計之編碼概述

2017-2-14

一、編碼概述

“編碼”是企業應用中很重要、也很常見的一個概念。在業務系統中,對幾乎所有的處理對象,都賦予一個唯一的代号,就像人的身份證号一樣,表示唯一的一個業務對象。具體講,比如公司代碼,部門代碼,員工代碼,物資代碼,科目代碼,運輸方式代碼,發票編号,訂單編号,報表編号,功能編号。等等。上面說了很多,幾乎涵蓋了業務系統中的從基礎資料、到業務單證,到系統管理的方方面面,可見,在系統的每個層次、每種角色,都要與編碼打交道。

同上,以上也提到了常見的業務對象,如公司,部門,科目,發票,報表等等。每個業務對象的屬性都很多。業務對象存放在資料庫中,屬性以列的方式存在(一般關系型資料庫),當然,編碼(以後我們統一稱為編碼,不使用代碼這個名詞了,這個名詞有歧義,有更廣泛的含義和用途)也是業務對象的一個屬性,而且是很重要的一個屬性。根據編碼,我們可以唯一定位一個業務對象。編碼就是業務對象的身份證,标簽。

二、如何編碼

如何編碼包括,編碼格式(使用數字,還是字母,還是允許特殊符号),編碼長度,編碼碼段的含義。具體如何編碼需要考慮實際的業務場景。這裡就幾個原則進行說明。

1. 編碼符号。可以使用 數字,字母 以及數字字母的組合。如果使用字母的話,最好不要區分大小寫。采取字母的一個缺點就是資料庫檢索的時候,要考慮字母的大小寫的問題,否則可能檢索失敗。編碼也可以采用除數字字母這之外的其他符号,比如減号(-),下劃線(_)。其他特殊符号、漢字就極少采用了。

采用哪些符号,主要是考慮輸入友善的問題,如果代碼不需要輸入或者很少輸入(比如訂單編号,極少人會根據訂單編号去查找一個訂單),就可以采用字母或者特殊符号。數字是最容易是輸入的符号,因為數字不區分大小寫,而且鍵盤上一般有專門的數字小鍵盤。熟練後,可以很快輸入。

2. 關于長度,考慮實際的編碼的容量。容量是隻編碼允許的最大數量。比如2位的純數字編碼,最多容納100個,00-99。在滿足編碼容量的基礎上行,不要太長,否則不容易記憶,尤其是一些經常需要輸入的編碼,比如物資編碼,部門編碼等等。

3,對一些系統自動生成、不需要輸入的代碼,可以适當長一些,比如guid就很長,可以作為唯一的代碼來使用。

4,編碼不要使用 0 打頭 ,因為在excel中,如果資料格式設定不對的話,0很可能給自動去掉了。因為excel會将純數字串,自動作為數字來處理,0自然就給去掉了。

5,編碼是否需要展現業務含義。

一般不建議。有時候,使用者希望能将業務u對象的某些屬性展現在編碼中,這樣通過編碼就可以知曉對象的某些屬性,比如我經手的一個收費項目,在确定住戶編碼規則時,業務組強烈要求在住戶編碼中将小區編号,樓棟号和門牌号放到編碼中,比如 10小區的9棟樓1單元12号的編碼為10090112。因為他們希望通過編碼就能看出住戶的位址,并且他們老系統就是這樣編碼的。技術組采納了這個建議,但是後來帶來一系列的麻煩,就是一旦建立了住戶,那麼住戶的樓棟号就不能修改了,否則編碼和樓棟号就不一緻了。再上線初期,由于基礎資料準備的有缺陷,導緻大量的使用者需要删除重建,就是因為位址搞錯了。是以這裡建議,編碼最好是無意義的,不要包含任意的業務屬性在裡邊。(注,GRG,20190428:糾正下,在上面的場景中,在編碼中包含樓棟号是有意義的,首先好記,其實最重要的是,樓号,小區号是穩定的,基本上是不變的,之是以提到的資料錯誤,是另外一碼事情,要解決這個問題,系統可以提供編碼變更的功能)。業務含義可以通過對象的屬性來表示。比如物資對象有物資名稱,規格型号,分類,廠商等。這些屬性放在物資編碼中就不合适。因為這些屬性是易變的。如果非要堅持,也隻能考慮那些不變的屬性。否則屬性發生了變化,編碼就要變。

還有一種表單編碼,很多客戶希望能展現表單類型,日期等屬性,比如訂單,DJ202101100001,表示2020年1月份10号的訂單。這個可以了解,但是一定不要用這個編碼做關鍵的業務判斷(比如根據訂單編碼來确定訂單生效日期),隻可以作為“大概”,“差不多”的視覺視覺就OK了,或者用在不是很嚴謹的查詢中(根據訂單号,查詢每天的訂單量)

6,編碼的自動生成與其他規則。

在系統實作時,一旦定義好了編碼規則,系統最好能根據編碼規則自動生成編碼,而不是讓使用者手工去輸入。當然資料遷移類項目例外,因為這種項目需要保持新老系統的編碼的一緻性。一般自動生成使用流水号。系統自動記錄某類業務對象的最大流水号,在建立時,自動加1。

有時候使用者可能希望編碼中展現日期屬性,這一般出現在業務單據編碼上,比如訂單編碼,使用者一般希望能以一下格式自動生成:YYYYMM+流水号。對這種穩定不變的編碼可以采用這種方式。

7,編碼是否可以重複

前面講過了,編碼作為業務對象的唯一标示,自然不允許重複,否則,兩個對象具有同樣的編碼,怎麼區分呢。如果編碼容量足夠大,這不是問題。但是凡事都有例外,如果編碼容量有限,而系統的生命周期又很長的話,多達幾年甚至更長,編碼容量可能會用盡,就像千年蟲一樣,本質上就是編碼容量不夠了,怎麼處理了。編碼唯一值相對的,是相對與一定的時間期間,比如我們可以定義一年内編碼不能重複,這樣通過将編碼+年度兩個屬相一起也能唯一确定一個業務對象。比如sap的憑證編号就是這樣的處理機制,一年一循環,今年的憑證編号與去年的肯定有重複的。但是這種處理方法,隻使用與動态業務資料,對生命周期比較長的,比如物資編碼類的,在系統生命周期之内,必須保持編碼的唯一性。

8,組織機構編碼

特别拿出組織結構編碼來讨論下,組織機構編碼在企業資訊化系統中是最重要的一個編碼之一,因為任何一個企業都有組織機構,人都屬于組織機構,業務的發生也是隸屬于每個組織機構的。組織機構往往是分級的。這樣組織機構的編碼就要展現處分級,這裡隻是簡單印出來,組織機構的進一步讨論有專門的章節。

9,登入賬号編碼

這個也是每個系統都有的。登入賬号編碼可以使用多種方式,一般村數值的用的比較多,固定一個長度,比如5位,10001.10002,這樣一次編碼。另外一個思路是,使用使用者的姓名縮寫,這樣的有點是好記。使用姓名縮寫的一個弊端就是,有可能存在重名的使用者。尤其是企業很大的,員工很多的集團新企業,存在重名的可能性太大了,解決方法,1是使用 姓全拼,名簡拼,比如諸葛亮的賬号就是 zhugl,張飛的就是zhangf。再就是使用姓名全拼,zhugeliang,zhangfei,但是這樣也不可避免同音重名,這樣隻能通過在後邊加上序号了,比如張飛,張峰,分别編碼為 zhangf,zhangf2。也可以加上身份證後4位來區分。根據個人身份(姓名和身份證)建立賬号的方式的優點是,使用者本人很容易知道自己的賬号,而不需要特别通知。原則就是容易記憶,也能處理重名的問題。

三,編碼的生成

一旦定義好了編碼規則,系統最好能根據編碼規則自動生成編碼。一般編碼中會有流水号,這樣系統可以記住每個業務對象的最大流水号,自動加1,形成最終的資料編碼。一般來講,不同的業務對象采用獨立的編碼規則和流水号,這樣能確定不同對象的編碼不會重複。當然這不是必須的。

前面講過了,既然編碼作為業務對象的唯一辨別,編碼就要唯一,不能重複。另一個角度講,考慮到編碼容量,盡量也不要浪費編碼,就是不要有空着不用的編碼。編碼最好做到連續。

不重複,可以通過全局流水号來控制,系統在生成編碼是首先取最大流水号,然後加1,形成新的流水号。

形成流水号的時機。通俗講,就是說核實形成流水号的,是在界面上建立的時候,還是在确确實實儲存到資料庫的時候呢。如果在界面上的時候,就存在浪費流水碼的問題,比如占用了一個号碼,但是最後沒有儲存,儲存時再形成就沒有這個問題。其實怎麼做都半斤八兩,即便在建立時做到了不浪費流水号,建立的資料删除後,這個号也就空出來了。還有一個方式是插号,就是形成流水号時,檢查前面時候有空的号,如果有,就直接拿過來用,否則,按照流水号的形成規則重新形成。這樣做有點複雜了,如果碼的容量沒有這麼緊張,就沒必要了。這種插号的方式在實際業務中也碰到過,一個是前幾年做的一個sap的接口的系統,sap的實施方中對訂單号的形成做了很變态的規定,每一位的含義都給規定好了,他們為了友善出報表(根據訂單号出報表),留給流水碼的寬度隻有3位,也就是如果隻是用數字的話,最多就是1000個訂單,這遠遠不夠。後來果然出現了編碼溢出的情況。這是sap實施方甩屁股走人了,留下客戶傻眼了,後來找到我們,我們采取了兩種措施:(1)插空,就是上面的方式,因為訂單号,存在空的情況。(2)流水号,不僅僅使用數字,還可以使用英文字母,這樣流水碼就從10進制進化成了36進制,編碼容量從1000擴充到36*36*36 = 46656,足足擴了46倍之多,完美解決了這個問題。

最後的建議,不用考慮号碼的連續性(如果沒有特别要求)。

四,編碼允許修改嗎

最後一個頭疼的問題,一旦編碼建立後,還允許修改嗎?

這卻是個難纏的問題,盡管經曆過這麼多項目,但是我現在還沒想清楚怎麼來回答這個問題。該吃飯了,我們吃完飯慢慢分析這個問題。

為什麼這是個問題?因為編碼是對象的唯一标示,如果修改了,會有一下問題:

1,已經被引用了。如果修改的話,導緻引用無效

如果考慮第一個原因,隻要編碼沒有被使用,就可以修改。如何判斷是否被引用,參見代碼引用關系一節。

還有一個變通的方法是,重新建立一個新編碼,然後将原編碼的狀态資料(如餘額資料)轉到新編碼。就像SAP一樣,資産編碼不能随便修改,因為資産編碼中包含類别資訊,他的做法就是重新建立一個新資産号,把資産價值等餘額資料通過價值轉儲轉到新資産上。

2,如果沒有被使用,可以修改。

我搞錯了,總不能不讓改吧。尤其是系統開始使用,初始化資料的時候,這種情況尤其常見。

3,修改後,同步修改被引用的資料。

比如部門編碼,人力資源部原本的編碼是101,後來修改為1101,那麼就把所有引用人力資源部編碼101 的表中的字段的值,修改為 1101。

六,内碼

對上一個問題,另外一個解決方法就是,對象除了編碼之外,系統再配置設定一個内部編碼給它,這個内部編碼(簡稱内碼)保持終生不變。内碼對使用者不可見,僅供系統使用。編碼是可見的,可以修改,對對象的引用使用内碼。這樣就解決了編碼調整的問題(編碼想怎麼該就怎麼改,内碼始終保持不變)。凡事有利必有弊,這樣也帶來了另外的一個麻煩,就是通過内碼引用時,在查詢時顯示編碼還需要根據内碼關聯查找,使程式變得複雜。同時也影響效率。關于内碼,還有個傳說,在我服務的一家公司(不妨稱之為G公司吧)的時候,G公司在投标的時候,就是這個内碼的概念,打敗了IBM。因為當時使用者就提出了一個問題,如果我的編碼在使用過後變化了怎麼辦。IBM給出的方案就是不能變,如果确實箱變,就建立一個編碼。G公司祭出内碼的概念,立馬忽悠住了客戶。

其實這個問題的本質就是,如何看待 内碼 和 編碼的關系,各是什麼含義。

如果你非要給編碼附上特别的含義,那編碼才有可能建立後修改的可能,否則如果僅僅是作為對象的辨別,不包含任何意義,根本就不需要改,是以就沒有内碼和編碼之争了。入庫過非要加上業務含義,那我們将編碼稱之為“唯一特征碼”,明确它的用法,這就能消除很多誤會;内碼恢複為id的本來面目。