数据库设计-规范化规则
SQL反模式一書在附錄章節給出了設計關系數據庫的規范化規則,一個簡明的規范化規則清單。
關系是什么
在規范化之前,我們先要理解下關系。
數學中關系定義:兩個不同數據域上的值的集合,通過一個條件得到的一個所有可能組合的子集。
怎么理解呢?書中以棒球隊和城市舉例,比如有一個包含所有棒球隊的集合,還有一個包含所有城市的集合,
如果把每個城市和每個球隊隨意組合進行列表,列表會很長,但其實我們只需要關注“球隊和其所屬城市的集合”。
所以“關系”:
然后將這個子集存儲在數據庫一張表中,關系即表示的是表中列與列之間的關系。
數據庫表需要滿足以下條件:
規范化的神話
以下一些對規范化的理解是錯誤的
很久以前我也是這么認為的,現在經過項目實踐以及閱讀SQL反模式這本書后,這些觀念都是不正確的,比如不規范只能讓某一種查詢變快,其他業務的查詢可能呢個就很困難和很慢,再比如不符合范式使得程序含有了隱藏的Bug。
規范化是保證我們正確的存儲數據,保證數據的完整性,如果沒有這個基礎,就不用談其他的了。
什么是規范化
書中簡明給出了規范化的目標如下:
提供數據的性能并不在以上列表中,但規范化本身保證數據的正確存儲,很多時候項目因為錯誤、不一致、重復的數據出現很多問題,但如果一開始就進行規范化設計,就會避免這些問題,所以在某種程度是提升了效率。
范式
以前只了解一到四范式以及BCNF范式,從本書了解到第六范式和DKNF范式,一般的講述都是以主鍵、候選鍵,主屬性、非主屬性以及依賴關系來解釋范式的,這里了解到不同的解釋。
?
第一范式
比如書中以產品缺陷管理舉例,如缺陷標簽,下面兩個圖都不滿足第一范式。
?
第一個表,tag1、tag2、tag3都是來自同一個集合tag
第二個表,tags字段的值為多個值也是來自同一個集合tag
始終記住:關系中的每一行,都是從多個集合的每個集合中選一個值形成的組合
滿足第一范式的如下圖:
?
第二范式
還是剛剛的缺陷標簽表,現在要往BugsTags表中增加打標簽者以及標簽創建者,下圖就不符合第二范式
?
tagger字段是打標簽者,coiner是標簽創建者,BugsTags表是bug_id和tag復合主鍵,coiner只與tag有關,與Bug是無關的,即只依賴部分主鍵,并且coiner取值是重復的,所以不符合第二范式。
如果需要修改某個標簽的創建者,如果沒有修改掉該表中所有tag為該標簽的coiner,就會出現數據不一致,這種情況尤其可能出現在多個人同時修改該值的時候。
但是我這里有一個疑問,這張表符合第一范式嗎?tagger和coiner字段都應該來賬戶集合(如Account表),有兩列來自相同的集合應該不符合第一范式呢。除非理解標簽創建者和打標簽者屬于不同集合。
而滿足第二范式的設計,是增加一個標簽表tags,如下圖:
?
?
第三范式
第二范式是存在字段只依賴于組合主鍵中的部分,而第三范式是存在字段不依賴于任何主鍵,而是依賴其他字段。
如果需要記錄處理Bug的工程師的email,下圖的設計不符合第三范式:
?
assigned_email與主鍵bug_id無關,只由assigned_to這個非主鍵字段決定,所以不符合第三范式,assigned_email取值也是冗余的,也會有更新問題。
符合第三范式的設計是應該把email放到Accounts表中,email直接與主鍵關聯沒有冗余。如下圖
?
BCNF(博伊斯-科德范式)
BCNF范式與第三范式的差異很細微,在第三范式中,所有非主鍵字段都必須直接依賴于這張表中的所有主鍵列,而BCNF范式要求主鍵字段也必須遵循這個規則,一般在一張表有復合主鍵時有效。
比如,我們有三種Tag類型,描述Bug所造成影響的tag,描述Bug影響子系統的tag,以及Bug修復狀態的tag,且要求每一個Bug對于每一種tag類型只能有一個tag,所以這里可能的復合主鍵有bug_id加上tag,或者復合主鍵為bug_id加上tag_type,這兩種組合都可以定位到一行。
存在兩種復合主鍵,所以下圖的設計就不符合BCNF,
?
這里一個隱含的假設是不同tag_type下的tag是不同的,且一個tag只屬于一種tag_type,如果沒有這個假設,bug_id加上tag是定位不到一行的。
按照這個假設,以前面符合第二范式和第三范式的設計很項,微妙的差別在于該字段在表中的作用是一般字段呢還是復合主鍵,符合第三范式的設計如下圖:
?
第四范式
??? 隨著業務應用復雜度的提升,比如需要支持多個用戶報告同一個Bug,并可以分配給多個開發工程師,然后由多個質量工程師驗證。這屬于多對多的關系,?? 所以我們需要額外的一張表,如下圖不符合第四范式:
?
這里不能單獨使用bug_id作為主鍵。每個Bug需要多行數據來實現各個字段都支持多個賬號的目的,主鍵需為bug_id、reported_by、assigned_to、verified_by的復合主鍵,然而bug報告時,是不需要立即進行分配和驗證的,所以assigned_to和verified_by需要可以為null,而標準情況下主鍵是不能為null的。
另外的一個問題就是當報告問題者大于分派者,或者大于驗證者就會出現數據冗余。
所以當只用一個表描述多個多對多關系時,就違背了第四范式,正確的做法如下圖:
?
第五范式
任何滿足BCNF范式并且沒有復合主鍵的表將同時滿足第五范式。
比如業務需要指定有些工程師只能為某些產品進行服務。
業務還需要了解哪些工程師在為哪些產品服務,以及修復了哪些Bug,下面的設計不符合第五范式
?
但這個表只說明了這個工程師當前指派去服務哪些產品,它不能說明哪個工程是可以被指派為哪些產品服務
與第四范式一樣,也是在一張表中存儲多種獨立的多對多關系而產生的,微妙的差別是沒有null的情況。
bug_id與assigned_to是多對多的關系,assigned_to與product_id是多對多的關系,而bug_id與product_id應該假定是多對一關系存在Bugs表中,每個Bug只屬于一個product。
所以解決方案是把每個多對多關系放在一個表中,如下:
?
DK范式
DK范式涵蓋了第三、四、五范式和BCNF范式。并認為表上的每個約束是這張表的數據域約束和主鍵約束的邏輯結果,如何理解呢?
比如狀態為新建的bug應該是沒有任何工作時間記錄的,也不需要指派驗證工程師的,一個做法是在狀態這個非主鍵字段上建立觸發器或者check約束,來進行驗證如果狀態為新建,沒有值是允許的,其他情況就不允許。但這種在非主鍵字段建議此約束的做法不符合DK范式。
第六范式
第六范式目標是消除所有的聯結依賴,通常用于支持記錄字段取值的變更歷史。
如Bug的狀態隨著時間推移產生變化,任何發生的變更,變更的時間,誰做的變更,以及其他可能的細節,需要另一張表進行記錄。
可以想象,如果Bugs這張表需要滿足第六范式,則需要變更的列都需要附帶一個歷史記錄表,會導致表的數量過多,多于大多業務來講,為每列變更都建立一個歷史記錄表是沒有必要的,在一些數據倉庫技術中會使用到第六范式。
個人體會:規范是為了減少數據冗余、提高數據的一致性必須的,二、三、四、BCNF、五范式的差異很微妙,隨著業務系統的復雜度的提升,需要考慮更高層次的范式。
轉載于:https://www.cnblogs.com/bwater/p/8203770.html
總結
以上是生活随笔為你收集整理的数据库设计-规范化规则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CoreJava学习第五课 --- 进入
- 下一篇: 大数据学习——虚拟机准备(改主机名)