資料庫設計優劣直接決定動態網站查詢效能與程式維護性。本文一次完整呈現從零狀態 (0NF) 經由第一、第二、第三正規化 (1NF/2NF/3NF) 的完整轉換過程,以「用戶-公司-收藏網址」案例實戰示範。
儲存用戶姓名、公司、公司地址、個人收藏網址。初始設計為單表多欄位。
users (0NF): name | company | company_address | url1 | url2 Joe | ABC | 1 Work Lane | abc.com | xyz.com Jill | XYZ | 1 Job Street | abc.com | xyz.com
痛點:URL 欄位固定數量、橫向擴展需改結構、查詢需多 OR 條件。
規則:消除重複群組、建立獨立表格、主鍵識別。
users (1NF): userId | name | company | company_address | url 1 | Joe | ABC | 1 Work Lane | abc.com 1 | Joe | ABC | 1 Work Lane | xyz.com 2 | Jill | XYZ | 1 Job Street | abc.com 2 | Jill | XYZ | 1 Job Street | xyz.com
改善:URL 垂直擴展無上限、查詢簡單化。代價:公司/用戶資訊重複儲存。
規則:重複欄位獨立成表、外鍵關聯。
轉換:抽離 urls 表。
users: userId | name | company | company_address 1 | Joe | ABC | 1 Work Lane 2 | Jill | XYZ | 1 Job Street urls: urlId | relUserId (FK→users.userId) | url 1 | 1 | abc.com 2 | 1 | xyz.com 3 | 2 | abc.com 4 | 2 | xyz.com
關聯:users.userId (PK) ← urls.relUserId (FK)
現況:URL 獨立管理、用戶主檔精簡。殘留問題:公司資訊仍重複 (ABC 出現 2 次,若 200 員工則 200 次)。
規則:非鍵欄位不相依於非鍵欄位。
分析:userId → company → company_address 為遞移相依。
轉換:抽離 companies 表。
users: userId | name | relCompId (FK→companies.compId) 1 | Joe | 1 2 | Jill | 2 companies: compId | company | company_address 1 | ABC | 1 Work Lane 2 | XYZ | 1 Job Street urls: (維持 2NF 結構) urlId | relUserId | url 1 | 1 | abc.com 2 | 1 | xyz.com 3 | 2 | abc.com 4 | 2 | xyz.com
companies (compId, PK)
│ 1
│ N
▼
users (userId PK, relCompId FK)
│ 1
│ N
▼
urls (urlId PK, relUserId FK)
若 URL 來自下拉選單 (有限選項),urls 表仍有重複 (abc.com 出現多次) → 需 4NF 抽離 URL 實體並建立 M:N 關聯表 (見進階篇)。
| 正規化 | 核心目標 | 關鍵動作 | 典型觸發場景 |
|---|---|---|---|
| 1NF | 原子性、主鍵 | 展開重複群組、加代理鍵 | 寬表、多值欄位、無主鍵 |
| 2NF | 消除部分相依 | 抽離複合主鍵部分相依欄位 | 複合 PK 表、非鍵相依部份 PK |
| 3NF | 消除遞移相依 | 抽離非鍵相依非鍵欄位 | 查詢/更新異常、實體混雜 |
| 4NF | 消除多值相依 | 抽離 M:N 關聯實體 | 多對多關聯、標籤/分類系統 |
| 5NF | 無損失分解 | 驗證連接重建原表 | 極複雜關聯網路 |
正規化非教條,乃依業務複雜度權衡結構清晰度、寫入效能與查詢靈活性的工具。理解各階段解決什麼問題,才能在實務中果斷決定「正規化到哪一層」。