【Normal Form】資料庫表結構設計所遵從的正規化
目錄
- 1. 第一正規化:原子性,不可再分
- 1.1. 是否為原子性
- 1.2. 典型的例子:多個資訊用分隔符拼接記錄
- 2. 第二正規化:非主鍵必須完全依賴於主鍵,而不能只依賴於主鍵的一部分
- 2.1. 不符合此特性的示例
- 3. 第三正規化:非主鍵必須直接依賴於主鍵,而不是傳遞依賴或間接依賴
- 3.1. 不符合此特性的示例
- 4. 後話
- 5. 參考的優秀文章
資料庫設計是件嚴肅、關鍵的事兒,一畢業,加入一個大型的行業專案,那兒的前輩資深工程師,就給我灌輸資料庫如何關鍵、神聖、深不可測的觀念,所以,我一直懷著崇拜的眼神。
幾年前,專案經理把一個小專案的資料庫設計工作交給我,我除了花費晚上和週末去完成。後來,更由於第一次負責整個系統的資料庫設計,更請教了以前公司的架構師哥們,幫我把把關,看自己有哪些木有想到的。
後來,將設計方案通過了評審,甚是高興,畢竟自己第一次設計一個系統的表結構,儘管,是一個小系統。
那麼,有了幾次資料庫表結構設計經驗,如何描述你設計表所遵循的原則呢?這時候,迴歸到大學課本,就是正規化(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正規化呢?
這不一定,要視具體情況,實際上,常見許多情況故意設定冗餘欄位使系統查詢更高效、更方便。