資料庫正規化 (Database Normalization)
正規化(Normalization)是資料庫設計的過程,目的是減少資料重複、避免異常情況(新增、更新、刪除異常),並確保資料的一致性。
正規化將資料表拆分成多個較小的表,並透過關聯(Relationship)連接它們。
為什麼需要正規化?
假設有一個未正規化的訂單資料表:
| 訂單編號 | 客戶名稱 | 客戶電話 | 產品名稱 | 產品價格 |
|---|---|---|---|---|
| 001 | 張一 | 02-1234 | 筆電 | 30000 |
| 002 | 張一 | 02-1234 | 滑鼠 | 500 |
| 003 | 王二 | 03-5678 | 筆電 | 30000 |
這個設計有以下問題:
- 資料重複:張一的電話重複儲存,筆電的價格重複儲存
- 更新異常:若張一換電話,需要更新多筆記錄
- 刪除異常:若刪除訂單 003,會連帶遺失王二的資料
- 新增異常:無法單獨新增一個還沒下訂單的客戶
第一正規化 (1NF)
規則:每個欄位都必須是原子值(不可再分割),且沒有重複的欄位群組。
違反 1NF 的例子:
| 訂單編號 | 產品 |
|---|---|
| 001 | 筆電, 滑鼠, 鍵盤 |
「產品」欄位包含多個值,違反了原子性。
符合 1NF:
| 訂單編號 | 產品 |
|---|---|
| 001 | 筆電 |
| 001 | 滑鼠 |
| 001 | 鍵盤 |
第二正規化 (2NF)
規則:符合 1NF,且所有非主鍵欄位都必須完全相依於主鍵(消除部分相依)。
假設主鍵是(訂單編號 + 產品編號):
| 訂單編號 | 產品編號 | 客戶名稱 | 產品名稱 | 數量 |
|---|
問題:「客戶名稱」只相依於「訂單編號」,「產品名稱」只相依於「產品編號」,這就是部分相依。
符合 2NF - 拆成三個表:
訂單表
| 訂單編號 | 客戶編號 |
|---|
產品表
| 產品編號 | 產品名稱 | 價格 |
|---|
訂單明細表
| 訂單編號 | 產品編號 | 數量 |
|---|
第三正規化 (3NF)
規則:符合 2NF,且所有非主鍵欄位都必須直接相依於主鍵(消除遞移相依)。
違反 3NF 的例子:
| 員工編號 | 員工名稱 | 部門編號 | 部門名稱 |
|---|
「部門名稱」相依於「部門編號」,而非直接相依於主鍵「員工編號」,這就是遞移相依。
符合 3NF - 拆成兩個表:
員工表
| 員工編號 | 員工名稱 | 部門編號 |
|---|
部門表
| 部門編號 | 部門名稱 |
|---|
正規化等級總結
| 正規化 | 要求 | 消除 |
|---|---|---|
| 1NF | 欄位值為原子值 | 重複群組 |
| 2NF | 符合 1NF + 完全相依於主鍵 | 部分相依 |
| 3NF | 符合 2NF + 直接相依於主鍵 | 遞移相依 |
反正規化 (Denormalization)
雖然正規化可以減少資料重複,但也會增加查詢時需要 JOIN 的表數量,可能影響效能。
在某些情況下,會刻意保留一些重複資料來提升查詢效率,這稱為反正規化。常見情境:
- 報表查詢需要快速存取
- 資料倉儲或 OLAP 系統
- 讀取遠多於寫入的應用
一般建議在設計階段先正規化到 3NF,之後再根據實際效能需求考慮是否反正規化。