引言
數據庫的設計范式是數據庫設計所需要滿足的規范滿足這些規范的數據庫是簡潔的結構明晰的同時不會發生插入(insert)刪除(delete)和更新(update)操作異常反之則是亂七八糟不僅給數據庫的編程人員制造麻煩而且面目可憎可能存儲了大量不需要的冗余信息
設計范式是不是很難懂呢?非也大學教材上給我們一堆數學公式我們當然看不懂也記不住所以我們很多人就根本不按照范式來設計數據庫
實質上設計范式用很形象很簡潔的話語就能說清楚道明白本文將對范式進行通俗地說明並以筆者曾經設計的一個簡單論壇的數據庫為例來講解怎樣將這些范式應用於實際工程
范式說明
第一范式(NF)數據庫表中的字段都是單一屬性的不可再分這個單一屬性由基本類型構成包括整型實數字符型邏輯型日期型等
例如如下的數據庫表是符合第一范式的
而這樣的數據庫表是不符合第一范式的
很顯然在當前的任何關系數據庫管理系統(DBMS)中傻瓜也不可能做出不符合第一范式的數據庫因為這些DBMS不允許你把數據庫表的一列再分成二列或多列因此你想在現有的DBMS中設計出不符合第一范式的數據庫都是不可能的
第二范式(NF)數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的情況)也即所有非關鍵字段都完全依賴於任意一組候選關鍵字
假定選課關系表為SelectCourse(學號 姓名 年齡 課程名稱 成績 學分)關鍵字為組合關鍵字(學號 課程名稱)因為存在如下決定關系
(學號 課程名稱) → (姓名 年齡 成績 學分)
這個數據庫表不滿足第二范式因為存在如下決定關系
(課程名稱) → (學分)
(學號) → (姓名 年齡)
即存在組合關鍵字中的字段決定非關鍵字的情況
由於不符合NF這個選課關系表會存在如下問題
() 數據冗余
同一門課程由n個學生選修學分就重復n次同一個學生選修了m門課程姓名和年齡就重復了m次
() 更新異常
若調整了某門課程的學分數據表中所有行的學分值都要更新否則會出現同一門課程學分不同的情況
() 插入異常
假設要開設一門新的課程暫時還沒有人選修這樣由於還沒有學號關鍵字課程名稱和學分也無法記錄入數據庫
() 刪除異常
假設一批學生已經完成課程的選修這些選修記錄就應該從數據庫表中刪除但是與此同時課程名稱和學分信息也被刪除了很顯然這也會導致插入異常
把選課關系表SelectCourse改為如下三個表
學生Student(學號 姓名 年齡)
課程Course(課程名稱 學分)
選課關系SelectCourse(學號 課程名稱 成績)
這樣的數據庫表是符合第二范式的消除了數據冗余更新異常插入異常和刪除異常
另外所有單關鍵字的數據庫表都符合第二范式因為不可能存在組合關鍵字
第三范式(NF)在第二范式的基礎上數據表中如果不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式所謂傳遞函數依賴指的是如果存在A → B → C的決定關系則C傳遞函數依賴於A因此滿足第三范式的數據庫表應該不存在如下依賴關系
關鍵字段 → 非關鍵字段x → 非關鍵字段y
假定學生關系表為Student(學號 姓名 年齡 所在學院 學院地點 學院電話)關鍵字為單一關鍵字學號因為存在如下決定關系
(學號) → (姓名 年齡 所在學院 學院地點 學院電話)
這個數據庫是符合NF的但是不符合NF因為存在如下決定關系
(學號) → (所在學院) → (學院地點 學院電話)
即存在非關鍵字段學院地點學院電話對關鍵字段學號的傳遞函數依賴
它也會存在數據冗余更新異常插入異常和刪除異常的情況讀者可自行分析得知
把學生關系表分為如下兩個表
學生(學號 姓名 年齡 所在學院)
學院(學院 地點 電話)
這樣的數據庫表是符合第三范式的消除了數據冗余更新異常插入異常和刪除異常
鮑依斯科得范式(BCNF)在第三范式的基礎上數據庫表中如果不存在任何字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式
假設倉庫管理關系表為StorehouseManage(倉庫ID 存儲物品ID 管理員ID 數量)且有一個管理員只在一個倉庫工作一個倉庫可以存儲多種物品這個數據庫表中存在如下決定關系
(倉庫ID 存儲物品ID) →(管理員ID 數量)
(管理員ID 存儲物品ID) → (倉庫ID 數量)
所以(倉庫ID 存儲物品ID)和(管理員ID 存儲物品ID)都是StorehouseManage的候選關鍵字表中的唯一非關鍵字段為數量它是符合第三范式的但是由於存在如下決定關系
(倉庫ID) → (管理員ID)
(管理員ID) → (倉庫ID)
即存在關鍵字段決定關鍵字段的情況所以其不符合BCNF范式它會出現如下異常情況
() 刪除異常
當倉庫被清空後所有存儲物品ID和數量信息被刪除的同時倉庫ID和管理員ID信息也被刪除了
() 插入異常
當倉庫沒有存儲任何物品時無法給倉庫分配管理員
() 更新異常
如果倉庫換了管理員則表中所有行的管理員ID都要修改
把倉庫管理關系表分解為二個關系表
倉庫管理StorehouseManage(倉庫ID 管理員ID)
倉庫Storehouse(倉庫ID 存儲物品ID 數量)
這樣的數據庫表是符合BCNF范式的消除了刪除異常插入異常和更新異常
范式應用
我們來逐步搞定一個論壇的數據庫有如下信息
() 用戶用戶名email主頁電話聯系地址
() 帖子發帖標題發帖內容回復標題回復內容
第一次我們將數據庫設計為僅僅存在表
這個數據庫表符合第一范式但是沒有任何一組候選關鍵字能決定數據庫表的整行唯一的關鍵字段用戶名也不能完全決定整個元組我們需要增加發帖ID回復ID字段即將表修改為
這樣數據表中的關鍵字(用戶名發帖ID回復ID)能決定整行
(用戶名發帖ID回復ID) → (email主頁電話聯系地址發帖標題發帖內容回復標題回復內容)
但是這樣的設計不符合第二范式因為存在如下決定關系
(用戶名) → (email主頁電話聯系地址)
(發帖ID) → (發帖標題發帖內容)
(回復ID) → (回復標題回復內容)
即非關鍵字段部分函數依賴於候選關鍵字段很明顯這個設計會導致大量的數據冗余和操作異常
我們將數據庫表分解為(帶下劃線的為關鍵字)
() 用戶信息用戶名email主頁電話聯系地址
() 帖子信息發帖ID標題內容
() 回復信息回復ID標題內容
() 發貼用戶名發帖ID
() 回復發帖ID回復ID
這樣的設計是滿足第范式和BCNF范式要求的但是這樣的設計是不是最好的呢?
不一定
觀察可知第項發帖中的用戶名和發帖ID之間是N的關系因此我們可以把發帖合並到第項的帖子信息中第項回復中的發帖ID和回復ID之間也是N的關系因此我們可以把回復合並到第項的回復信息中這樣可以一定量地減少數據冗余新的設計為
() 用戶信息用戶名email主頁電話聯系地址
() 帖子信息用戶名發帖ID標題內容
() 回復信息發帖ID回復ID標題內容
數據庫表顯然滿足所有范式的要求
數據庫表中存在非關鍵字段標題內容對關鍵字段發帖ID的部分函數依賴即不滿足第二范式的要求但是這一設計並不會導致數據冗余和操作異常
數據庫表中也存在非關鍵字段標題內容對關鍵字段回復ID的部分函數依賴也不滿足第二范式的要求但是與數據庫表相似這一設計也不會導致數據冗余和操作異常
由此可以看出並不一定要強行滿足范式的要求對於N關系當的一邊合並到N的那邊後N的那邊就不再滿足第二范式了但是這種設計反而比較好!
對於MN的關系不能將M一邊或N一邊合並到另一邊去這樣會導致不符合范式要求同時導致操作異常和數據冗余
對於的關系我們可以將左邊的或者右邊的合並到另一邊去設計導致不符合范式要求但是並不會導致操作異常和數據冗余
結論
滿足范式要求的數據庫設計是結構清晰的同時可避免數據冗余和操作異常這並意味著不符合范式要求的設計一定是錯誤的在數據庫表中存在或N關系這種較特殊的情況下合並導致的不符合范式要求反而是合理的
在我們設計數據庫的時候一定要時刻考慮范式的要求
From:http://tw.wingwit.com/Article/program/SQL/201311/16402.html