范式
是用一組規則定義的數據庫設計標準,旨在確保數據庫結構合理,避免數據冗余和異常。
目的
- 消除數據的重復,提高存儲效率
- 防止數據異常(插入、刪除、更新異常)
- 提高數據的完整性和一致性
第一范式
- 定義
- 所有列(字段)必須是原子性的(不可拆分的基本數據單位)
- 表中的每個字段都應包含原子值,不能存儲集合、數組等多值數據
- 要求
- 每個字段只存儲單一值,無重復列
- 每一行是唯一的(通常用主鍵)
舉例:
- 不符合1NF:一列存多個電話號碼,用逗號分隔
- 比如: 一個“電話號碼”字段存著“13812345678,13987654321”兩個號碼(多個值在一列)
- 符合1NF:用多個字段存放各個電話號碼,或用一行存一個電話
第二范式
- 定義
- 在1NF基礎上,消除表中的部分依賴(部分依賴是指非主鍵列依賴于主鍵的一部分)
- 要求
- 所有非主鍵列必須完全依賴于整個主鍵(對于復合主鍵)
舉例:
- 表:訂單明細(order_id, product_id, product_name)
- 關鍵點:
- 如果主鍵是(order_id, product_id)
- product_name 依賴product_id,不是整個主鍵 → 不符合2NF
- 解決方案:將product_name單獨放到產品表
第三范式
- 定義
- 在2NF基礎上,消除非主鍵列之間的傳遞依賴
- 要求
- 所有非主鍵列都必須直接依賴于主鍵,不依賴于其他非主鍵列
舉例:
- 表:員工(員工ID,部門ID,部門名稱)
- 依賴關系:
- 部門名稱依賴于部門ID
- 部門ID依賴于員工ID
- 由于部門名稱依賴于部門ID(非主鍵),這是傳遞依賴,不符合3NF
- 解決方案:將部門信息單獨拆分到新表
- 依賴關系:
更高階的范式
- BCNF(Boyce-Codd Normal Form):比3NF更嚴格,要求每個決定因素都是超鍵
- 4NF、5NF:更復雜,涉及多值依賴和連接依賴,較少在日常開發中應用
如何進行判斷依賴和主鍵
判斷依賴
-
- 依賴關系的基本概念
- 依賴:某個字段(或多個字段)值的確定依賴于另一個字段(或多個字段)
- 簡述:
- 如果字段B的值都是由字段A的值唯一決定的,稱為“B依賴A”
- 記作:A → B
-
- 如何識別依賴關系?
- 分析業務邏輯:理解數據的實際關系
- 觀察數據的函數關系:
- 比如:
- 一個“訂單ID”決定了“訂單日期” → 訂單ID → 訂單日期
- 某個“學生ID”決定“學生姓名” → 學生ID → 姓名
- 借助示意圖:
- 可以畫出依賴關系圖(如:箭頭指向依賴方)
訂單ID | 訂單日期 | 客戶ID | 客戶名 |
---|
- 依賴關系:
- 訂單ID → 訂單日期
- 客戶ID → 客戶名
- 推斷:
- 訂單ID決定訂單日期
- 客戶ID決定客戶名
判斷主鍵
- 觀察每一列(字段):
- 哪個字段的值在整個表中是唯一的?(比如:身份證號、學號、訂單編號)
- 組合判斷:
- 如果單個字段不能唯一標識一條記錄,就考慮多個字段結合
- 比如:
- “訂單ID”唯一 → 訂單ID就是主鍵
- 但:
- “訂單ID + 商品ID”,這個組合也能唯一標識一條訂單中的商品項
- 實際操作:
- 查看每個字段的值,識別唯一性
- 使用數據庫設計工具或SQL語句:
select count(distinct 某字段) from 表名;
- 如果結果等于總記錄數,說明該字段唯一。
- 當單一字段不能唯一標識行時,用多個字段組合作為主鍵
- 如:
- 學生-課程關聯表:學生ID + 課程ID
- 判斷依據:聯合唯一性
總結
事項 | 方法 | 舉例 |
---|---|---|
依賴關系 | 分析業務邏輯或觀察數據中的“唯一決定關系” | 訂單ID → 訂單日期 |
確定主鍵 | 查看哪些字段的值在表中唯一 | 訂單ID是唯一則單字段主鍵 |
組合主鍵 | 多個字段合成唯一標識 | 學生ID + 課程ID組成主鍵 |
示例
學生ID | 課程ID | 課程名 | 教師名 |
---|---|---|---|
1 | 101 | 數學 | 老師A |
2 | 102 | 語文 | 老師B |
1 | 102 | 語文 | 老師B |
-
滿足1NF:每個字段都是單一值
-
是否滿足2NF?
- 主鍵可能是(學生ID + 課程ID)
- 課程名和教師名只依賴課程ID,不是整個復合主鍵 → 不滿足2NF。
- 解決:拆兩張表——
- 學生選課表:學生ID,課程ID
- 課程表:課程ID,課程名,教師名
-
是否滿足3NF?
- 課程表只有課程相關信息,沒有傳遞依賴。
示例2:
反范式
:為了提高查詢速度或簡化復雜查詢,有意將部分冗余數據引入設計,放寬或取消范式規范。
目的
- 目標:
- 減少多表連接(JOIN)操作
- 提升讀取性能,尤其在大數據量和高并發場景
- 簡化應用層的數據訪問邏輯
何時采用反范式
- 查詢頻繁涉及復雜多表JOIN,影響性能
- 讀操作遠多于寫操作(寫入時維護冗余數據帶來額外成本)
- 實時性要求高,不能接受延遲
- 業務場景對數據一致性要求相對較低(允許一定的冗余和同步)
例子:
- 不反范式(正常化設計):
- 查詢訂單和訂單詳情需要多表JOIN
- 代碼中拼接復雜SQL,邏輯繁瑣
- 采用反范式:
- 將訂單詳情部分數據直接存儲在訂單表里
- 查詢只需訪問一張表,邏輯簡單,代碼清晰
常見方式
- 將關聯的表合并成一張表,避免JOIN操作
- 在主表中添加冗余字段,如冗余姓名、類別等
- 復制部分數據到不同的表中,方便快速查詢
風險與管理
- 數據不一致:冗余數據不同步可能導致數據差異
- 數據不一致主要是因為冗余數據在多個位置存儲后,沒有保持同步,導致各個副本或字段中的信息出現差異。
為什么會導致數據不一致?
-
- 缺少同步機制
- 在反范式設計中,冗余數據通常存儲在多個表或位置。
- 如果沒有有效的同步策略(如觸發器、業務邏輯或應用程序控制同步),在數據變更時,某些冗余字段沒有及時更新。
-
- 寫操作不完整或遺漏
- 更新、刪除、插入操作中,某些地方的冗余數據沒有一起更新。
- 例如:更新訂單狀態時,只更改訂單表中的狀態字段,但未同步更新訂單的詳細視圖或歷史記錄。
-
- 并發修改造成的沖突
- 多個事務同時修改不同副本,可能導致不同步或出現“最后寫入”覆蓋的問題。
-
- 缺少約束或觸發器
- 沒有設置約束(如觸發器、外鍵關系)來保證冗余字段在數據變更時自動同步。
-
- 業務邏輯缺陷
- 業務程序未考慮數據同步問題,導致不同的數據源或存儲之間數據不同。
-
維護成本增加:更新時需要同步多份數據
-
設計復雜度:需要額外的代碼邏輯保證數據一致性
因此,反范式應謹慎使用,只在確有性能瓶頸或特殊需求時采用
實際應用
- 應用建議:
- 在設計數據庫時,遵循至少3NF,可以最大程度降低數據冗余和異常
- 但也會帶來更多的表,查詢可能變復雜,性能可能降低
- 常用過程中會在范式和性能之間做權衡(有時會適當范式反范,即反范式化)
總結
范式 | 目的 | 核心原則 | 典型表現 |
---|---|---|---|
1NF | 原子性 | 每個字段只存基本值 | 沒有多值字段 |
2NF | 消除部分依賴 | 非主鍵都依賴整個主鍵 | 對復合主鍵特別重要 |
3NF | 消除傳遞依賴 | 非主鍵不依賴于其他非主鍵 | 避免冗余存儲冗余數據 |
反范式 | 動機 | 典型做法 | 潛在問題 |
---|---|---|---|
反范式 | 提升性能,簡化查詢 | 添加冗余字段、合并表 | 可能導致數據不一致 |
-
范式是符合某一種級別的關系模式的集合。構造數據庫必須遵循一定的規則。在關系數據庫中,這種規則就是范式
- 范式優點:減少了數據冗余,數據表更新操作快、占用存儲空間少
- 范式缺點:查詢時通常需要多表關聯查詢,更難進行索引優化
-
反范式的過程就是通過冗余數據來提高查詢性能,但冗余數據會犧牲數據一致性
- 反范式優點:所有的數據都在同一張表中,可以減少表關聯,更好進行索引優化
- 反范式缺點:存在大量冗余數據,數據維護成本更高