亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

1. 前言

在面向對象語言中涉及到諸多的設計模式,例如單例模式、適配器模式,設計模式的存在是為了讓系統中的代碼邏輯更加清晰,幫助開發者建立更加健壯的系統,同時滿足易修改特性和易擴展特性。數據庫設計時也存在類似設計模式的通用規范,被稱為數據庫范式。滿足范式的數據庫是簡潔的,表與表之間的關系也清晰且明確,不會存儲過多的冗余信息,在增刪改查的時候也可以避免冗余的操作。

2. 數據庫設計三大范式

面試官提問: 請描述下數據庫設計的三大范式?

題目解析: 回答本題時,可以從總分的結構來闡述,即先闡述數據庫范式的定義,再挨個解釋每種范式的設計原則。

數據庫范式定義:為了建立邏輯結構合理、冗余較小的數據庫,在設計數據表時必須要遵循的設計規范。

接下來可以分點闡述第一、第二、第三范式的定義和案例。

2.1 數據庫第一范式(1NF)

數據庫第一范式是設計數據庫時需要滿足的最基本范式:

① 定義:第一范式(First Normal Form)要求數據庫表中的所有字段都是不可拆分的原子字段,換句話說,每個字段不可以再進行拆分。

② 案例解釋:對于一張最簡單的用戶信息表,定義了用戶編號、姓名、年齡、電話這三個字段,user_info表如下:

用戶編號(user_id) 姓名(username) 年齡(age) 電話(phone)
1 小明 20 10086
2 小紅 21 10087
3 小王 22 10088

其中電話(phone)這個字段可能存儲的是座機電話號碼、也可能是手機電話號碼,定義上并不明確,這就違背了第一范式的原子性。所以為了滿足第一范式,我們可以將電話字段拆分為座機電話(fixed_phone)和手機電話(cell_phone)兩個字段,拆分后的user_info表如下:

用戶編號(user_id) 姓名(username) 年齡(age) 座機電話(fixed_phone) 手機電話(cell_phone)
1 小明 20 10086 18010002000
2 小紅 21 10087 18010002001
3 小王 22 10088 18010002002

③ 范式優點:拆分之后,字段定義定義清晰。在查詢數據庫時我們可以明確過濾的是座機號碼還是手機號碼,方便業務層邏輯開發,而且后續維護也方便。

2.2 數據庫第二范式(2NF)

在滿足第一范式的基礎上,數據庫第二范式對字段定義進行了更嚴格的約束:

① 定義:第二范式(Second Normal Form)要求數據庫中的每一列都和主鍵相關,不能和主鍵的一部分相關。

② 案例解釋:在電商環境下,我們需要設計一個訂單表,因為訂單和商品綁定, 所以將商品編號和訂單編號作為訂單表的聯合主鍵,初始設計的訂單(order)表如下:

訂單編號(order_id) 商品編號(good_id) 購買數量(order_num) 單位(unit) 商品單價(good_price) 購買時間(purchase_time)
10001 8888 1 千克 100 2020-10-11
10002 8888 1 千克 100 2020-10-12
10003 8890 3 300 2020-10-13

仔細觀察,我們就能發現這種設計的問題在于:good_id = 8888的商品,對于order_id = 10001和10002記錄都存儲了相同的單位和商品價格,這種冗余存儲在數據量大的場景下是不能接收的,并且違反了第二范式設計原則,商品價格只和商品編號有關,和訂單編號無關,我們將這張表進行拆分:

拆分的原則是:將屬于商品的信息單獨提煉為一張商品表,在原有的訂單表只保留商品編號作為聯合查詢時的查詢依據,優化后的訂單(order)表如下:

訂單編號(order_id) 商品編號(good_id) 購買數量(order_num) 購買時間(purchase_time)
10001 8888 1 2020-10-11
10002 8888 1 2020-10-12
10003 8889 3 2020-10-13

單獨拆分出的商品(good)表如下:

商品編號(good_id) 單位(unit) 商品單價(good_price)
8888 千克 100
8889 300

③ 范式優點:拆分之后,降低了數據庫的冗余存儲,并且邏輯清晰,要查詢商品信息即走good表,要查詢訂單信息即走order表。

2.3 數據庫第三范式(3NF)

① 定義:第三范式(Third Normal Form)要求數據庫表中的每個字段和主鍵都直接相關,不能間接相關。

② 案例解釋:還是以第一范式中的user_info表作為案例,如果要存儲每個用戶的省份和省會城市,我們可能會設計出下面這樣一張表:

用戶編號(user_id) 姓名(username) 年齡(age) 座機電話(fixed_phone) 手機電話(cell_phone) 省份(province) 省會城市(city)
1 小明 20 10086 18010002000 北京市 北京市
2 小紅 21 10087 18010002001 黑龍江省 哈爾濱市
3 小王 22 10088 18010002002 貴州省 貴陽市

我們將用戶編號(user_id)作為主鍵,則姓名、年齡、座機電話、手機電話都和"用戶"這個主體強相關,和主鍵直接相關,而省份和省會城市則和"用戶"這個主體是弱相關,和主鍵間接相關,并且存在依賴關系:用戶編號 -> 姓名,姓名 -> 省份,省份 -> 省會城市,這樣構建了用戶編號 -> 省會城市的間接傳遞關系,這種關系會導致數據冗余,而且在執行刪除/修改/增加操作的時候,會產生異常情況:刪除所有"貴州省"下的用戶信息(即user_id = 3的記錄),"貴州省"和"貴陽市"的信息也被刪除了(顯然不合理,因為省份這個定義和省份下的人員記錄并沒有關系)。

所以我們需要將user_info表拆分,我們通過省份構建數據關系,優化后的用戶(user_info)表如下:

用戶編號(user_id) 姓名(username) 年齡(age) 座機電話(fixed_phone) 手機電話(cell_phone) 省份(province)
1 小明 20 10086 18010002000 北京市
2 小紅 21 10087 18010002001 黑龍江省
3 小王 22 10088 18010002002 貴州省

獨立拆分出的省份(province)表如下:

省份(province) 省會城市(city)
北京市 北京市
黑龍江省 哈爾濱市
貴州省 貴陽市

③ 范式優點:提高了表的獨立性,降低數據存儲冗余。

3. 小結

作為開發,在日常設計數據庫表的時候可能不會特意注意使用數據庫范式,但是細心關注大部分企業項目的表結構,就會發現大部分表都是遵循數據庫范式設計的,第二范式和第三范式可能會混淆概念,第二范式的核心是關注非主鍵列是否依賴主鍵或者主鍵的一部分,第三范式的核心是關注非主鍵列是否依賴主鍵,還是依賴其他的非主鍵列。