數據庫設計三范式(NF)之第二范式極簡說明
一句話核心思想
第二范式就是:一張表只干一件事。
更專業一點說:確保表中的每列都完全依賴于整個主鍵,而不是只依賴于主鍵的一部分。
用一個生動的例子來解釋
想象一下,我們有一張 訂單明細表 來記錄超市的購物小票。
| 訂單ID (主鍵) | 商品ID (主鍵) | 商品名稱 | 單價 | 數量 | 顧客姓名 | 顧客電話 |
|---|---|---|---|---|---|---|
| 1001 | A01 | 可口可樂 | 3.5 | 2 | 張三 | 13800138000 |
| 1001 | B02 | 樂事薯片 | 8.0 | 1 | 張三 | 13800138000 |
| 1002 | A01 | 可口可樂 | 3.5 | 5 | 李四 | 13900139000 |
這張表有什么問題?(它違反第二范式)
-
冗余重復:
-
同一個訂單ID(1001)出現了兩次,顧客“張三”和他的電話也被重復存儲了。
-
商品“可口可樂”的單價(3.5)在多個訂單中重復出現。如果可樂漲價了,我們需要修改表中所有包含了“A01”商品的行,非常容易出錯和遺漏。
-
-
更新異常:
-
如果張三換了手機號,我必須找到所有
訂單ID=1001的記錄,一條一條地去修改顧客電話,而不能只修改一次。
-
-
插入異常:
-
如果有一個新商品“C03-農夫山泉”剛剛入庫,但還沒有任何訂單購買過它,我們就無法把這個商品的信息(名稱、單價)插入到這張表里。因為缺少主鍵的另一部分(訂單ID)。
-
-
刪除異常:
-
如果訂單1001的“樂事薯片”這條記錄被刪除了,我們可能就徹底丟失了“張三”這個顧客的信息,因為他只有一個訂單。
-
為什么會這樣?
因為這張表不止干了一件事!它混雜了:
-
訂單的詳細信息(哪個訂單買了哪個商品,買了多少)
-
商品自身的信息(商品叫什么,賣多少錢)
-
顧客的信息(誰買的,聯系方式)
它的主鍵是(訂單ID, 商品ID),這是一個聯合主鍵。
-
數量完全依賴于整個主鍵(我必須同時知道訂單號和商品號,才能確定買了幾件)。 -
但是
顧客姓名和顧客電話只依賴于訂單ID。只要我知道訂單號1001,我就知道顧客是張三,不需要知道商品ID是什么。 -
同樣,
商品名稱和單價只依賴于商品ID。只要我知道商品ID是A01,我就知道它是可口可樂,價格3.5元,不需要知道是哪個訂單買的。
這種“只依賴于部分主鍵”的列,就是違反第二范式的根源。
如何改造?(使其符合第二范式)
核心操作:拆表!讓每張表只干一件事。
我們把上面那張大雜燴表拆成三張表:
1. 訂單表 (專門管訂單和顧客的關系)
| 訂單ID (主鍵) | 顧客姓名 | 顧客電話 |
|---|---|---|
| 1001 | 張三 | 13800138000 |
| 1002 | 李四 | 13900139000 |
2. 商品表 (專門管商品信息)
| 商品ID (主鍵) | 商品名稱 | 單價 | |
|---|---|---|---|
| A01 | 可口可樂 | 3.5 | |
| B02 | 樂事薯片 | 8.0 | |
| C03 | 農夫山泉 | 2.0 | (新商品可以獨立添加了!) |
3. 訂單明細表 (專門管訂單買了什么商品)
| 訂單ID (聯合主鍵) | 商品ID (聯合主鍵) | 數量 |
|---|---|---|
| 1001 | A01 | 2 |
| 1001 | B02 | 1 |
| 1002 | A01 | 5 |
改造后的好處:
-
數據冗余大大減少:顧客信息只存一次,商品信息只存一次。
-
避免更新異常:張三換手機號,只需在
訂單表里修改一次。可樂漲價,只需在商品表里修改一次。 -
避免插入異常:新商品“農夫山泉”可以直接添加到
商品表,哪怕它還沒被賣出過。 -
避免刪除異常:即使刪除了訂單1001的薯片記錄,張三的信息依然安全地保存在
訂單表里。
總結:如何判斷和滿足第二范式?
-
第一步:確保你的表已經滿足了第一范式(每個字段都是不可再分的原子值)。
-
第二步:看看你的表有沒有聯合主鍵(由多個字段組成的主鍵)。
-
第三步:檢查表中的每一個字段,問自己:
“這個字段是完全依賴于整個主鍵,還是只依賴于主鍵中的一部分?”
-
第四步:如果發現只依賴于部分主鍵的字段,就把它們拆出去,放到另一張表里,并為它們建立新的主鍵。
最終記住那個簡單的比喻:一個好的數據庫設計就像一個好的公司架構,每個部門(表)職責單一,專業高效,而不是一個部門什么都管,混亂不堪。

浙公網安備 33010602011771號