1. 程式人生 > >【Normal Form】資料庫表結構設計所遵從的正規化

【Normal Form】資料庫表結構設計所遵從的正規化

目錄

資料庫設計是件嚴肅、關鍵的事兒,一畢業,加入一個大型的行業專案,那兒的前輩資深工程師,就給我灌輸資料庫如何關鍵、神聖、深不可測的觀念,所以,我一直懷著崇拜的眼神。

幾年前,專案經理把一個小專案的資料庫設計工作交給我,我除了花費晚上和週末去完成。後來,更由於第一次負責整個系統的資料庫設計,更請教了以前公司的架構師哥們,幫我把把關,看自己有哪些木有想到的。

後來,將設計方案通過了評審,甚是高興,畢竟自己第一次設計一個系統的表結構,儘管,是一個小系統。

 

那麼,有了幾次資料庫表結構設計經驗,如何描述你設計表所遵循的原則呢?這時候,迴歸到大學課本,就是正規化(Normal Form)。

 

1. 第一正規化:原子性,不可再分

原子性,即,欄位應該是不可再分的。

 

1.1. 是否為原子性

如,一個系統的地址用於郵寄商品,那麼下表的ADDRESS是符合第一正規化的。

ID USER_NAME AGE PHONE ADDRESS
1 Nick Huang 18 12345678 深圳市羅湖區地王大廈1003室

 

而如何判斷是否原子性,這需要根據實際的業務判斷。

比如一些系統僅根據地址作送貨服務的,則使用上述的結構即滿足第一正規化;而某些系統,地址除了用於送貨,還需要對使用者所在地區分佈做長期的統計,為了統計方便,上面的ADDRESS設計就不符合原子性了,也許應該為:

ID USER_NAME AGE PHONE PROVINCE CITY ADDRESS
1 Nick Huang 10 12345678 廣東省 深圳市 地王大廈1003室

 

1.2. 典型的例子:多個資訊用分隔符拼接記錄

還有其他一些典型的不符合原子性的,就是將多個數據放在一個欄位中了。

如Phone欄位:

ID USER_NAME AGE PHONE
1 Nick Huang 10 23658745,25654150

 

還有這種情況,如EXT_FIELDS欄位:

ID USER_NAME AGE PHONE ADDRESS EXT_FIELDS
1 Nick Huang 10 12345678 深圳市XXX <DATA><JOB>Programmer</JOB><PASSPORT>12345678</PASSPORT></DATA>

 

2. 第二正規化:非主鍵必須完全依賴於主鍵,而不能只依賴於主鍵的一部分

非主鍵必須完全依賴於主鍵,而不能只依賴於主鍵的一部分(聯合主鍵)

 

2.1. 不符合此特性的示例

博文《許可權管理系統概要設計》有一系列使用者許可權的表,如果以此為例子,將其中的表結構設計為如下,則不符合第二正規化:

USER_ID、ROLE_ID為主鍵,描述使用者和角色的關聯關係;STATUS描述這個關聯關係是生效還是失效。

可以看出,STATUS是描述這段關聯的,是依賴USER_ID、ROLE_ID的,即完全依賴於主鍵。

而USER_NAME是使用者名稱稱,它只依賴於USER_ID,即只依賴於主鍵的一部分。USER_NAME欄位的設定不符合第二正規化。

 

如果有一天,使用者的名稱需要修改,那麼就要修改與此使用者相關的每一筆關聯的資料。

 

3. 第三正規化:非主鍵必須直接依賴於主鍵,而不是傳遞依賴或間接依賴

非主鍵必須直接依賴於主鍵,而不是傳遞依賴或間接依賴。

 

3.1. 不符合此特性的示例

博文《許可權管理系統概要設計》有一系列使用者許可權的表,如果以此為例子,將其中的表結構設計為如下,則不符合第三正規化:

此為角色表,ID為角色ID,NAME為角色名稱,STATUS為此角色是否生效,SYSTEM_ID為此角色所屬的系統ID,SYSTEM_NAME為此角色所屬的系統的名稱。

可以看出SYSTEM_NAME為傳遞依賴,在角色表中SYSTEM_ID依賴與ID,而SYSTEM_NAME有依賴與SYSTEM_ID。SYSTEM_NAME欄位的設定不符合第三正規化。

 

4. 後話

是不是資料庫設計一定得嚴格遵守3正規化呢?

這不一定,要視具體情況,實際上,常見許多情況故意設定冗餘欄位使系統查詢更高效、更方便。