如何解釋關係數據庫的第一第二第三範式?

本文轉自知乎劉慰教師

範式介紹

首先要明白”範式(NF)”是什麼意思。按照教材中的定義,範式是“符合某一種級別的關係模式的集合,表示一個關係內部各屬性之間的聯繫的合理化程度”。很晦澀吧?實際上你可以把它粗略地理解爲一張數據表的表結構所符合的某種設計標準的級別。就像家裏裝修買建材,最環保的是E0級,其次是E1級,還有E2級等等。數據庫範式也分爲1NF,2NF,3NF,BCNF,4NF,5NF。一般在我們設計關係型數據庫的時候,最多考慮到BCNF就夠。符合高一級範式的設計,必定符合低一級範式,例如符合2NF的關係模式,必定符合1NF。

第一範式

1NF的定義爲:符合1NF的關係中的每個屬性都不可再分。

符合1NF的關係(你可以理解爲數據表。“關係模式”和“關係”的區別,類似於面向對象程序設計中”類“與”對象“的區別。”關係“是”關係模式“的一個實例,你可以把”關係”理解爲一張帶數據的表,而“關係模式”是這張數據表的表結構。
表1所示的情況,就不符合1NF的要求:
表1

實際上,1NF是所有關係型數據庫的最基本要求,你在關係型數據庫管理系統(RDBMS),例如SQL Server,Oracle,MySQL中創建數據表的時候,如果數據表的設計不符合這個最基本的要求,那麼操作一定是不能成功的。也就是說,只要在RDBMS中已經存在的數據表,一定是符合1NF的
如果我們要在RDBMS中表現表中的數據,就得設計爲表2的形式:
表2
但是僅僅符合1NF的設計,仍然會存在數據冗餘過大,插入異常,刪除異常,修改異常的問題,例如對於表3中的設計:
表3

  1. 每一名學生的學號、姓名、系名、系主任這些數據重複多次。每個系與對應的系主任的數據也重複多次——數據冗餘過大
  2. 假如學校新建了一個系,但是暫時還沒有招收任何學生(比如3月份就新建了,但要等到8月份才招生),那麼是無法將系名與系主任的數據單獨地添加到數據表中去的 1——插入異常
  1. 假如將某個系中所有學生相關的記錄都刪除,那麼所有系與系主任的數據也就隨之消失了(一個系所有學生都沒有了,並不表示這個系就沒有了)。——刪除異常
  2. 假如李小明轉系到法律系,那麼爲了保證數據庫中數據的一致性,需要修改三條記錄中系與系主任的數據。——修改異常
    正因爲僅符合1NF的數據庫設計存在着這樣那樣的問題,我們需要提高設計標準,去掉導致上述四種問題的因素,使其符合更高一級的範式(2NF),這就是所謂的“規範化”。

第二範式

2NF在1NF的基礎之上,消除了非主屬性對於碼的部分函數依賴。

第二範式(2NF)在關係理論中的嚴格定義我這裏就不多介紹了(因爲涉及到的鋪墊比較多),只需要瞭解2NF對1NF進行了哪些改進即可。其改進是,2NF在1NF的基礎之上,消除了非主屬性對於碼的部分函數依賴。接下來對這句話中涉及到的四個概念———__“函數依賴”、“碼”、“非主屬性”、與“部分函數依賴” __進行一下解釋。

函數依賴

我們可以這麼理解(但並不是特別嚴格的定義):若在一張表中,在屬性(或屬性組)X的值確定的情況下,必定能確定屬性Y的值,那麼就可以說Y函數依賴於X,寫作 X → Y。也就是說,在數據表中,不存在任意兩條記錄,它們在X屬性(或屬性組)上的值相同,而在Y屬性上的值不同。這也 就是“函數依賴”名字的由來,類似於函數關係 y = f(x),在x的值確定的情況下,y的值一定是確定的。

例如,對於表3中的數據,找不到任何一條記錄,它們的學號相同而對應的姓名不同。所以我們可以說姓名函數依賴於學號,寫作 學號 → 姓名。但是反過來,因爲可能出現同名的學生,所以有可能不同的兩條學生記錄,它們在姓名上的值相同,但對應的學號不同,所以我們不能說學號函數依賴於姓名。

表中,其他的函數依賴關係還有如:

  • 系名 → 系主任
  • 學號 → 系主任
  • (學號,課名) → 分數

但以下函數依賴關係則不成立:

  • 學號 → 課名
  • 學號 → 分數
  • 課名 → 系主任
  • (學號,課名)→ 姓名
    從“函數依賴”這個概念展開,還會有三個概念:
完全函數依賴

在一張表中,若 X → Y,且對於 X 的任何一個真子集(假如屬性組 X 包含超過一個屬性的話),X ’ → Y 不成立,那麼我們稱 Y 對於 X 完全函數依賴,記作:
無
例如: 學號 F→ 姓名 (學號,課名) F→ 分數 (注:因爲同一個的學號對應的分數不確定,同一個課名對應的分數也不確定)

部分函數依賴

假如 Y 函數依賴於 X,但同時 Y 並不完全函數依賴於 X,那麼我們就稱 Y 部分函數依賴於 X,記作 :
在這裏插入圖片描述
例如:(學號,課名) P→ 姓名

傳遞函數依賴

假如 Z 函數依賴於 Y,且 Y 函數依賴於 X (基於『Y 不包含於 X,且 X 不函數依賴於 Y』這個前提),那麼我們就稱 Z 傳遞函數依賴於 X ,記作:
在這裏插入圖片描述

設 K 爲某表中的一個屬性或屬性組,若除 K 之外的所有屬性都完全函數依賴於 K(這個“完全”不要漏了),那麼我們稱 K 爲候選碼,簡稱爲碼。
在實際中我們通常可以理解爲:假如當 K 確定的情況下,該表除 K 之外的所有屬性的值也就隨之確定,那麼 K 就是碼。
一張表中可以有超過一個碼。(實際應用中爲了方便,通常選擇其中的一個碼作爲主碼
例如:對於表3,(學號、課名)這個屬性組就是碼。該表中有且僅有這一個碼。(假設所有課沒有重名的情況)

非主屬性

包含在任何一個碼中的屬性成爲主屬性。
例如:
對於表3,主屬性就有兩個,學號 與 課名。

終於可以回過來看2NF了。
首先,我們需要判斷,表3是否符合2NF的要求?
根據2NF的定義,判斷的依據實際上就是看數據表中是否存在非主屬性對於碼的部分函數依賴。若存在,則數據表最高只符合1NF的要求,若不存在,則符合2NF的要求。
判斷的方法是:
第一步:找出數據表中所有的碼。
第二步:根據第一步所得到的碼,找出所有的主屬性。
第三步:數據表中,除去所有的主屬性,剩下的就都是非主屬性了。
第四步:查看是否存在非主屬性對碼的部分函數依賴。
對於表3,根據前面所說的四步,我們可以這麼做
第一步:查看所有每一單個屬性,當它的值確定了,是否剩下的所有屬性值都能確定。查看所有包含有兩個屬性的屬性組,當它的值確定了,是否剩下的所有屬性值都能確定。……查看所有包含了六個屬性,也就是所有屬性的屬性組,當它的值確定了,是否剩下的所有屬性值都能確定。看起來很麻煩是吧,但是這裏有一個訣竅,就是假如A是碼,那麼所有包含了A的屬性組,如(A,B)、(A,C)、(A,B,C)等等,都不是碼了(因爲作爲碼的要求裏有一個“完全函數依賴”)。
圖4表示了表中所有的函數依賴關係:
圖4

這一步完成以後,可以得到,表3的碼只有一個,就是(學號、課名)。
第二步:主屬性有兩個:學號 與 課名
第三步:非主屬性有四個:姓名、系名、系主任、分數
第四步:對於(學號,課名) → 姓名,有 學號 → 姓名,存在非主屬性 姓名 對碼(學號,課名)的部分函數依賴。
對於(學號,課名)→ 系名,有 學號 → 系名,存在非主屬性 系名 對碼(學號,課名)的部分函數依賴。
對於(學號,課名) → 系主任,有 學號 → 系主任,存在非主屬性 系主任 對碼(學號,課名)的部分函數依賴。所以表3存在非主屬性對於碼的部分函數依賴,最高只符合1NF的要求,不符合2NF的要求。

部分函數依賴

爲了讓表3符合2NF的要求,我們必須消除這些部分函數依賴,只有一個辦法,就是將大數據表拆分成兩個或者更多個更小的數據表,在拆分的過程中,要達到更高一級範式的要求,這個過程叫做”模式分解“。
模式分解的方法不是唯一的,以下是其中一種方法:
選課(學號,課名,分數)
學生(學號,姓名,系名,系主任)
我們先來判斷以下,選課表與學生表,是否符合了2NF的要求?
對於選課表,其碼是(學號,課名),主屬性是學號和課名,非主屬性是分數,學號確定,並不能唯一確定分數,課名確定,也不能唯一確定分數,所以不存在非主屬性分數對於碼 (學號,課名)的部分函數依賴,所以此表符合2NF的要求。
對於學生表,其碼是學號,主屬性是學號,非主屬性是姓名、系名和系主任,因爲碼只有一個屬性,所以不可能存在非主屬性對於碼 的部分函數依賴,所以此表符合2NF的要求。
圖5表示了模式分解以後的新的函數依賴關係
圖5
下圖,表示了模式分解以後新的數據:
在這裏插入圖片描述

(這裏還涉及到一個如何進行模式分解纔是正確的知識點,先不介紹了)

現在我們來看一下,進行同樣的操作,是否還存在着之前的那些問題?

  1. 李小明轉系到法律系只需要修改一次李小明對應的系的值即可。——有改進
  2. 數據冗餘是否減少了?學生的姓名、系名與系主任,不再像之前一樣重複那麼多次了。——有改進
  3. 刪除某個系中所有的學生記錄該系的信息仍然全部丟失。——無改進
  4. 插入一個尚無學生的新系的信息。因爲學生表的碼是學號,不能爲空,所以此操作不被允許。——無改進

所以說,僅僅符合2NF的要求,很多情況下還是不夠的,而出現問題的原因,在於仍然存在非主屬性系主任對於碼學號的傳遞函數依賴。爲了能進一步解決這些問題,我們還需要將符合2NF要求的數據表改進爲符合3NF的要求。

第三範式

3NF在2NF的基礎之上,消除了非主屬性對於碼的傳遞函數依賴。

3NF在2NF的基礎之上,消除了非主屬性對於碼的傳遞函數依賴,也就是說, 如果存在非主屬性對於碼的傳遞函數依賴,則不符合3NF的要求。接下來我們看看錶4中的設計,是否符合3NF的要求。
對於選課表,主碼爲(學號,課名),主屬性爲學號和課名,非主屬性只有一個,爲分數,不可能存在傳遞函數依賴,所以選課表的設計,符合3NF的要求。對於學生表,主碼爲學號,主屬性爲學號,非主屬性爲姓名、系名和系主任。因爲 學號 → 系名,同時 系名 → 系主任,所以存在非主屬性系主任對於碼學號的傳遞函數依賴,所以學生表的設計,不符合3NF的要求。
爲了讓數據表設計達到3NF,我們必須進一步進行模式分解爲以下形式:
選課(學號,課名,分數)
學生(學號,姓名,系名)
系(系名,系主任)

對於選課表,符合3NF的要求,之前已經分析過了。
對於學生表,碼爲學號,主屬性爲學號,非主屬性爲系名,不可能存在非主屬性對於碼的傳遞函數依賴,所以符合3NF的要求。
對於系表,碼爲系名,主屬性爲系名,非主屬性爲系主任,不可能存在非主屬性對於碼的傳遞函數依賴(至少要有三個屬性纔可能存在傳遞函數依賴關係),所以符合3NF的要求。
新的依賴關係如圖:
在這裏插入圖片描述

新的數據如表:
在這裏插入圖片描述

現在我們來看一下,進行同樣的操作,是否還存在着之前的那些問題?

  1. 刪除某個系中所有的學生記錄,該系的信息不會丟失。——有改進
  2. 插入一個尚無學生的新系的信息, 因爲系表與學生表目前是獨立的兩張表,所以不影響。——有改進
  3. 數據冗餘更加少了。——有改進

結論

由此可見,符合3NF要求的數據庫設計,基本上解決了數據冗餘過大,插入異常,修改異常,刪除異常的問題。當然,在實際中,往往爲了性能上或者應對擴展的需要,經常 做到2NF或者1NF,但是作爲數據庫設計人員,至少應該知道,3NF的要求是怎樣的。

BCNF範式

BCNF範式不存在主屬性對於碼的部分函數依賴與傳遞函數依賴

要了解 BCNF 範式,那麼先看這樣一個問題:
若:某公司有若干個倉庫;每個倉庫只能有一名管理員,一名管理員只能在一個倉庫中工作;一個倉庫中可以存放多種物品,一種物品也可以存放在不同的倉庫中。每種物品在每個倉庫中都有對應的數量。那麼關係模式 倉庫(倉庫名,管理員,物品名,數量) 屬於哪一級範式?
答:
**函數依賴集:**倉庫名 → 管理員,管理員 → 倉庫名,(倉庫名,物品名)→ 數量
碼:(管理員,物品名),(倉庫名,物品名)
**主屬性:**倉庫名、管理員、物品名
**非主屬性:**數量
∵ 不存在非主屬性對碼的部分函數依賴和傳遞函數依賴。∴ 此關係模式屬於3NF。
基於此關係模式的關係(具體的數據)可能如圖所示:

在這裏插入圖片描述
好,既然此關係模式已經屬於了 3NF,那麼這個關係模式是否存在問題呢
我們來看以下幾種操作:
先新增加一個倉庫,但尚未存放任何物品,是否可以爲該倉庫指派管理員?
——不可以,因爲物品名也是主屬性,根據實體完整性的要求,主屬性不能爲空。
某倉庫被清空後,需要刪除所有與這個倉庫相關的物品存放記錄,會帶來什麼問題?
——倉庫本身與管理員的信息也被隨之刪除了。
如果某倉庫更換了管理員,會帶來什麼問題?
——這個倉庫有幾條物品存放記錄,就要修改多少次管理員信息。
從這裏我們可以得出結論:
在某些特殊情況下,即使關係模式符合 3NF 的要求,仍然存在着插入異常,修改異常與刪除異常的問題,仍然不是 ”好“ 的設計。
造成此問題的原因:
存在着主屬性對於碼的部分函數依賴與傳遞函數依賴。(在此例中就是存在主屬性【倉庫名】對於碼【(管理員,物品名)】的部分函數依賴。解決辦法就是要在 3NF 的基礎上消除主屬性對於碼的部分與傳遞函數依賴:
倉庫(倉庫名,管理員)
庫存(倉庫名,物品名,數量)
這樣,之前的插入異常,修改異常與刪除異常的問題就被解決了。
以上就是關於 BCNF 的解釋。


  1. 根據三種關係完整性約束中實體完整性的要求,關係中的所包含的任意一個屬性都不能爲空,所有屬性的組合也不能重複。爲了滿足此要求,圖中的表,只能將學號與課名的組合作爲碼,否則就無法唯一地區分每一條記錄。[:關係中的某個屬性或者某幾個屬性的組合,用於區分每個元組(可以把“元組”理解爲一張表中的每條記錄,也就是每一行)]。 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章