在數據庫中,經常會有這幾個字段:ID,Code,Name,代表序號、編碼(編號)和名稱。比如學生,有學號和姓名,一般還有一個唯一性的ID,通過這個ID就一定不會找到2個或更多的學生。
現在的問題是,當A表指向B表,A表應該記錄B表的哪個字段?
舉幾個例子:
1. 用戶有一個屬性是“性別”,用戶表應該記錄性別表的ID?Code?還是Name?
2. 訂單有一個屬性是下單用戶,訂單表應該記錄用戶表的ID?Code?還是Name?
記錄Name
記錄Name是指兩個表通過Name建立關聯。由於Name常有重復,比如人有同名同姓,屬於不同上級的名稱經常有重復,全國有許多個地方都叫“新鄉”,所以,記錄Name就會有二義性。記錄Name的方案被排除。這個大家觀點基本一致。但是如果是指向類似性別這種字典/術語型的,在保證Name不重復的情況下,可以記錄Name,而且記錄Name更直觀。
爭論比較大的是,記錄ID還是Code?有人說有時記ID,有時記Code,所以有了進一步追問:什么情況下應該記錄ID,什么情況應該記錄Code?
記錄ID
這里ID等同主鍵,通常是整數或字符串。數據庫會約束它一定不能為null,一定不會重復,即是具有非空性+唯一性。通過一個ID查找數據,只能找到一條數據,或者找不到,但絕對不會找到兩條(或更多)數據。而且ID天然帶索引,查找速度快。ID值本身沒有意義,如果是整數通常會設置成自增長,即是1,2,3這么增加上去。如果要支持分開多個庫,ID還要不重復,自增長就不合適,或者實現起來復雜,靈活度也不高。代替的方案如GUID(UUID),或雪花算法及其變種,特點都是值很長(18位十進制,16或者32個字符等)。值無意義且太長導致使用有諸多不便。當需要作映射等適配功能時,ID就非常困難。比如雙方約好ID=1代表男,ID=2代表女,結果到某個舊系統中一看剛好反過來,此時需要通過映射來糾正這種對應關系。如果直接將舊系統的性別表的“男”“女”記錄的ID相互調換,則與外部系統是一致了,但與內部其它表(也是引用ID)就剛好相反了。所以,記錄ID沒有二義性,但缺少靈活性。
記錄Code
引用(使用)方和被引用(定義)方約好了一套Code,雙方按照這個Code值建立聯系。比如員工有工號,物流單有單號。這里有個問題,Code不像ID天然具有唯一性,但可以加約束禁止重復,實際情況的Code也不應該允許重復,比如兩個員工的工號相同,兩個物流單的單號相同,都是要出問題的。還有個問題,Code可能為空,比如剛報到的員工暫時還沒有工號,這個問題后面再談。相對於ID,Code的優勢是比較明顯的,它往往按照一定的含義編制而成,值具有意義,就比較好記。如果用作與外部系統關聯,效果則要看內部表之間如何關聯。假如內部表是也是用Code關聯,則當需與外部系統作映射時,會遇到和ID一樣的問題,系統內外兩頭不能同時解決。假如內部是用ID關聯,則可以通過調整ID與Code的關系,協調好內部的對應與外部的對應,內外可以同時解決。還是前面那個性別的例子,只需要修改舊系統里“男”的Code為1,“女”的Code為2,就好。因為系統內部引用的是ID,Code的修改不會影響系統的內部關系,卻能與外部系統建立正確的關聯,從而同時解決了系統內部的對應問題。
結論
到這里結論就比較清晰了:
1. 系統內部用ID關聯。非空,唯一。
2. 系統間用Code關聯。值有意義,靈活可映射。
更多的思考
沒有工號的員工怎么辦?
規定只要是員工必須有工號。如果正式工號的產生需要有流程,可以考慮給臨時工號。就像上路的汽車必須有車牌,沒有正式牌也得有臨時牌。
感謝 螺絲釘 協助閱稿並提出修改建議。