[原創(chuàng)] 基礎(chǔ)中的基礎(chǔ)(三):理解數(shù)據(jù)庫的幾種鍵和幾個(gè)范式
在上學(xué)的時(shí)候,數(shù)據(jù)庫是一門讓我比較頭大的課程。記得當(dāng)時(shí)教材上凈是一些晦澀難懂的語言,沒有充足的實(shí)例來幫助理解。前一陣子在看《網(wǎng)絡(luò)游戲服務(wù)器端編程》的過程中,突然對(duì)數(shù)據(jù)庫范式有了一些感覺,在此總結(jié)一下,分享給大家。作者純菜鳥,即使總結(jié)這些基礎(chǔ)知識(shí)也難免有錯(cuò),希望給位大牛不吝賜教,謝謝!
鍵(關(guān)系鍵)以及數(shù)據(jù)庫范式都是關(guān)系數(shù)據(jù)庫的概念。所謂關(guān)系鍵,指的是一個(gè)表中的一個(gè)(或一組)屬性,用來 標(biāo)識(shí)該表的每一行 或 與另一個(gè)表產(chǎn)生聯(lián)系 。
數(shù)據(jù)庫的”范式“,指的是 設(shè)計(jì)數(shù)據(jù)庫的規(guī)則 。按照一定的規(guī)則設(shè)計(jì)出數(shù)據(jù)庫的表和關(guān)系,能夠避免在一些情況下的查詢出錯(cuò),并具有良好的結(jié)構(gòu)。 總的來說,隨著范式等級(jí)的提高, 數(shù)據(jù)表屬性之間的依賴關(guān)系越來越小,數(shù)據(jù)冗余越來越低。 但同時(shí),數(shù)據(jù)關(guān)系變得更加復(fù)雜,訪問一個(gè)具體數(shù)據(jù)的關(guān)系層次增加。 所以像設(shè)計(jì)模式一樣,不應(yīng)盲目追求范式等級(jí),應(yīng)根據(jù)具體需求來選擇范式。
?
我們先來看一下幾種常見的數(shù)據(jù)庫關(guān)系鍵:
1、 超鍵(super key) :能夠 唯一標(biāo)識(shí) 一條記錄的屬性或?qū)傩约?
-
- 標(biāo)識(shí)性:一個(gè)數(shù)據(jù)表的所有記錄都具有不同的超鍵
- 非空性:不能為空
2、候選鍵( candidate key ) :能夠 唯一標(biāo)識(shí) 一條記錄的 最小屬性集
-
- 標(biāo)識(shí)性:一個(gè)數(shù)據(jù)表的所有記錄都具有不同的候選鍵
- 最小性:候選鍵的 任何子集 都不能唯一標(biāo)識(shí)一個(gè)記錄
- 非空性:不能為空
- 候選鍵是 沒有多余屬性 的超鍵
3、主鍵(主碼、primary key) : 某個(gè) 能夠 唯一標(biāo)識(shí) 一條記錄的 最小屬性集
-
- 唯一性:一個(gè)數(shù)據(jù)表只能有一個(gè)主鍵
- 標(biāo)識(shí)性:一個(gè)數(shù)據(jù)表的所有記錄都具有不同的主鍵取值
- 非空性:不能為空
- 選取某個(gè)候選鍵為主鍵
4、 外鍵(foreign key) :子數(shù)據(jù)表中出現(xiàn)的 父數(shù)據(jù)表的主鍵 ,稱為子數(shù)據(jù)表的外鍵。
5、代理鍵 :當(dāng)不適合用任何一個(gè)候選鍵作為主鍵時(shí)(如數(shù)據(jù)太長等),添加一個(gè)沒有實(shí)際意義的鍵作為主鍵,這個(gè)鍵就是代理鍵。(如常用的序號(hào)1、2、3)
6、自然鍵 :自然生活中唯一能夠標(biāo)識(shí)一條記錄的鍵(如身份證)
?
下面就來看一下常見的幾種關(guān)系數(shù)據(jù)庫范式吧。
?
一、第一范式(1NF)
要求:
- 每一個(gè)屬性都不能再分割 ,都是原子項(xiàng)。
第一范式是關(guān)系型數(shù)據(jù)表的基本要求,但是如何判斷一個(gè)屬性能否再分割呢?這沒有統(tǒng)一的標(biāo)準(zhǔn),需要 依照需求確定 。比如,我們要設(shè)計(jì)一個(gè)網(wǎng)絡(luò)游戲后臺(tái)所用的數(shù)據(jù)庫,其中有一個(gè)數(shù)據(jù)表,記錄有關(guān)于擊殺怪物所獲得的金錢:
編號(hào) |
怪物名 |
掉落金錢 |
1 |
巨熊 |
100 |
? 這個(gè)表格看上去并沒有什么問題。每一個(gè)屬性項(xiàng)都是”不可分割“的,所以符合第一范式。但是,如果我們希望把玩家擊殺怪物之后獲得的金錢分成兩部分,一部分是固定收益,另一部分是一個(gè)隨機(jī)的浮動(dòng)收益(比如和玩家幸運(yùn)值有關(guān))。則這張表格中的”掉落金錢“項(xiàng)就不是”不可分割“了,也就不符合第一范式了。如果有這種需求,我們就可以把”掉落金錢“分割為”固定金錢“和”浮動(dòng)金錢“兩部分。如下所示:
編號(hào) |
怪物名 |
固定金錢 |
浮動(dòng)金錢 |
1 |
巨熊 |
80 |
20 |
這樣分割之后,使得每一項(xiàng)都不能再分割,從而使得數(shù)據(jù)表滿足第一范式。?
滿足第一范式的數(shù)據(jù)表有什么好處呢?
- 1NF保證了數(shù)據(jù)庫的每一列都是不同的。每一列的數(shù)據(jù)彼此沒有任何交集。
- 這樣做首先 減少了數(shù)據(jù)的冗余,節(jié)省存儲(chǔ)空間 。如果不滿足第一范式,一些數(shù)據(jù)項(xiàng)有可能包含相同的”子項(xiàng)“,造成存儲(chǔ)空間的浪費(fèi)。
- 其次,每一列沒有重復(fù)的數(shù)據(jù)意味著 不需要考慮數(shù)據(jù)更新的同步問題 。不用擔(dān)心在一列中更新了數(shù)據(jù),還要在另一列做相應(yīng)修改。
- 另外,每一列的數(shù)據(jù)不可再分, 在某些情況下 減 少了數(shù)據(jù)訪問的層數(shù),提高數(shù)據(jù)訪問速度。
?
二、第二范式(2NF)
要求:
- 滿足第一范式
- 非主鍵屬性均完全依賴于主鍵
? 非主鍵屬性和主鍵可以有什么關(guān)系?1、完全依賴。2、部分依賴。3、不依賴(沒關(guān)系)。顯然第三種情況下,這個(gè)屬性就不應(yīng)該放在這張數(shù)據(jù)表中。所以2NF要求非主鍵屬性完全依賴于主鍵,就是在 消除非主鍵屬性對(duì)主鍵的 部分函數(shù)依賴。 既然是部分函數(shù)依賴,暗含著說主鍵是一個(gè)復(fù)合鍵(由多個(gè)屬性組成的鍵)。如果某個(gè)非主鍵屬性只和主鍵中的一部分有關(guān)(部分函數(shù)依賴),則不符合第二范式。舉例,網(wǎng)絡(luò)游戲的用戶數(shù)據(jù)表:
玩家用戶名 |
角色名 |
角色職業(yè) |
上次登錄時(shí)間 |
?Alice |
superman |
wizard |
2013-11-4? |
如果我們的游戲允許一個(gè)玩家擁有多個(gè)角色,則在這張表中“玩家用戶名”和“角色名”構(gòu)成復(fù)合主鍵,唯一標(biāo)識(shí)一條記錄。表中的“角色職業(yè)”,與玩家用戶名和角色名均相關(guān),為完全依賴于主鍵。而“上次登錄時(shí)間”僅和“玩家用戶名”相關(guān),而與角色名無關(guān)。所以“上次登錄時(shí)間” 部分函數(shù)依賴于主鍵。 本關(guān)系不符合2NF。
要將上表轉(zhuǎn)換為符合2NF的結(jié)構(gòu)也很簡(jiǎn)單,只要把部分函數(shù)依賴的部分抽出來,組成新的表即可。如下所示:
玩家用戶名 |
角色名 |
角色職業(yè) |
Alice |
superman |
wizard |
?
玩家用戶名 |
上次登錄時(shí)間 |
Alice |
2013-11-4 |
符合2NF能給我們帶來什么好處呢?2NF消除了屬性對(duì)主鍵的部分函數(shù)依賴。
首先, 2NF可以在一定程度上消除冗余,節(jié)省存儲(chǔ)空間。
如果存在部分函數(shù)依賴,則可能存在數(shù)據(jù)冗余。在多條記錄中,主鍵中的某一個(gè)屬性可能是一樣的,而如果有其他數(shù)據(jù)項(xiàng)函數(shù)依賴于這個(gè)不變的屬性,則這些數(shù)據(jù)項(xiàng)也將是一樣的。比如在上面例子中,在修改之前的表中,如果有多個(gè)角色名對(duì)應(yīng)一個(gè)玩家用戶名,則會(huì)有多條數(shù)據(jù)。它們具有一樣的用戶名和不同的角色名。由于上次登錄時(shí)間僅依賴于玩家用戶名,所以在這多條記錄中,上次登錄時(shí)間也都是相同的,造成了冗余。
其次, 2NF 簡(jiǎn)化了表的邏輯關(guān)系 ,使得表的結(jié)構(gòu)更加清晰。
?
三、第三范式(3NF)
要求:
- 滿足第一、二范式
- 所有非主鍵屬性之間沒有函數(shù)依賴關(guān)系
3NF在2NF的基礎(chǔ)上, 進(jìn)一步消除 非主鍵屬性之間 的函數(shù)依賴關(guān)系 。實(shí)質(zhì)上,也是消除非主鍵屬性中的傳遞依賴。更進(jìn)一步地說,如果兩個(gè)數(shù)據(jù)表有關(guān)系。那么這兩個(gè)數(shù)據(jù)表中的非主鍵屬性必須是不同的。如果存在一個(gè)非主鍵屬性A,存在于兩張表中。則在某張表中,A依賴于外鍵,從而不符合3NF。比如網(wǎng)絡(luò)游戲中拍賣行的數(shù)據(jù),可以按照下面的表格進(jìn)行存儲(chǔ):
玩家姓名 |
物品名 |
單價(jià) |
數(shù)量 |
總金額 |
Alice |
治療藥劑 |
50 |
10 |
500 |
在這個(gè)表格中,“總金額”項(xiàng)可以通過“單價(jià)”和“數(shù)量”運(yùn)算得出,存在函數(shù)依賴關(guān)系,不滿足3NF。
要將這個(gè)表格修改為滿足3NF的要求,只需要從表中刪除“總金額”即可。在另外一些情況中,可以將函數(shù)依賴關(guān)系涉及到的項(xiàng)單獨(dú)抽出來組成新的表,需要具體情況具體分析。
3NF的優(yōu)點(diǎn)很明顯, 可以減少數(shù)據(jù)冗余,節(jié)省存儲(chǔ)空間。 既然存在函數(shù)依賴,某些數(shù)據(jù)項(xiàng)就能夠通過其他數(shù)據(jù)項(xiàng)計(jì)算得出,很可能存在數(shù)據(jù)冗余。值得注意的是,在一些情況下, 存在這種數(shù)據(jù)冗余的表格是有意義的 。如果在表格中存儲(chǔ)著某些運(yùn)算的結(jié)果,我們?cè)谑褂眠@些結(jié)果時(shí)就不用進(jìn)行運(yùn)算了,節(jié)省了運(yùn)算時(shí)間,是一種“空間換時(shí)間”的做法。從這里也可以看出,應(yīng)用范式并不能夠保證最好的效果,需要根據(jù)應(yīng)用需求進(jìn)行合理取舍。
??
四、BC范式(boyce-codd范式,BCNF)
要求:
- 滿足1NF、2NF、3NF?
- 所有屬性(包含主鍵屬性和非鍵主屬性)都 不傳遞依賴于任何候選鍵
BC范式在3NF的基礎(chǔ)上,要求主鍵屬性也不能傳遞依賴于任何候選鍵。當(dāng)主鍵是復(fù)合鍵是,主鍵的某個(gè)屬性可能會(huì)依賴于某個(gè)候選鍵。此時(shí),關(guān)系能夠符合3NF,因?yàn)椴⒉皇恰胺侵麈I”屬性依賴于某個(gè)非主鍵屬性。但此關(guān)系并不符合BC范式。例如,在以房間為組織方式的游戲中,我們記錄某個(gè)玩家、房間和房主的關(guān)系。
房主ID |
房間ID |
玩家ID |
Alice |
123 |
Bob |
表中的依賴關(guān)系有:
- (玩家ID,房間ID)-> 房主ID
- 房主ID -> 房間ID
- (玩家ID,房主ID)-> 房間ID
同時(shí),表中的候選鍵有(玩家ID,房間ID)、(玩家ID,房主ID)。比如,我們選擇主鍵為(玩家ID,房間ID),那么,房間ID就是主鍵的一個(gè)屬性。而在依賴關(guān)系2中,房間ID依賴于房主ID,房主ID是候選鍵(玩家ID,房主ID)的一個(gè)屬性。那么,首先, 由于房間ID不是候選鍵屬性,所以此表并沒有違反3NF。 但是由于房間ID和房主ID存在依賴關(guān)系,所以滿足“ 主鍵屬性傳遞依賴于某個(gè)候選鍵 ”的條件,所以此表不符合BC范式。
要把上表修改為滿足BC范式的形式,只要把它進(jìn)行合理拆分即可。
房間ID |
玩家ID |
123 |
Bob |
?
房間ID |
房主ID |
123 |
Alice |
? BC范式的好處是 進(jìn)一步消除了表中的依賴關(guān)系,減少了冗余 。例如在上例中,如果我們采用未修改的版本,如果想要存儲(chǔ)一個(gè)10個(gè)玩家(不含房主)的房間,就需要10條這樣的記錄才可以。
??
五、第四范式
要求:
- 滿足1NF、2NF、3NF
- 表中不能包含一個(gè)實(shí)體的兩個(gè)或多個(gè)多值屬性
所謂多值屬性,指的是 某個(gè)屬性可以包含多個(gè)值 。這個(gè)屬性的(多個(gè))取值,被另一個(gè)屬性決定。也就是說,一旦確定了某個(gè)屬性,另一個(gè)屬性的多個(gè)取值就一起確定了。第四范式在第三范式的基礎(chǔ)上, 消除多值依賴 。所謂多值依賴,指的是一組值(多值屬性)依賴于另一個(gè)屬性。函數(shù)依賴是一對(duì)一的關(guān)系,多值依賴是一對(duì)多的關(guān)系。這個(gè)理解起來我感覺有點(diǎn)別扭,可能我的理解也有偏差,說出來和大家一起探討一下。
比如,我們要在數(shù)據(jù)庫中保存玩家的角色技能信息,這里我們?cè)试S一個(gè)玩家具有多個(gè)角色,一個(gè)角色具有多個(gè)技能:
?
玩家ID |
角色名 |
技能 |
Alice |
superman |
Fire ball |
首先,這個(gè)表只有一個(gè)候選鍵(玩家ID、角色名、技能)。所以肯定符合3NF。進(jìn)一步觀察一下,玩家ID是一個(gè)單值屬性。角色名就是一個(gè)多值屬性了,因?yàn)橐粋€(gè)玩家ID可以對(duì)應(yīng)多個(gè)角色名。角色名在表中看起來是一項(xiàng),這是由于受制于具體數(shù)據(jù)庫提供的功能。邏輯上,我們拿到一個(gè)玩家ID,可以確定的是,這個(gè)玩家具有某些角色,是一個(gè)一對(duì)多的關(guān)系,是多值依賴。角色名是一個(gè)多值屬性。同樣的,一個(gè)角色也對(duì)應(yīng)著多個(gè)技能,這也是多值依賴。技能也是一個(gè)多值屬性。顯然,這個(gè)表并不符合4NF。
這個(gè)表有什么問題呢?
首先, 數(shù)據(jù)冗余大 ,如果一個(gè)玩家有好幾個(gè)具有Fire Ball技能的角色,這個(gè)技能項(xiàng)就要重復(fù)保存幾次。
其次, 增、刪、改都比較復(fù)雜 ,比如我們要?jiǎng)h除Fire Ball技能,那么,我們要?jiǎng)h除這個(gè)玩家所有具有Fire Ball技能的表項(xiàng)。
要將上表修改為符合4NF的表,只需要將多值依賴進(jìn)行合理映射即可:
玩家ID |
角色名 |
Alice |
superman |
?
角色名 |
技能 |
superman |
Fire ball |
這兩個(gè)表都符合4NF。
可以看出,4NF的使用可以 降低數(shù)據(jù)冗余,并且減少數(shù)據(jù)處理復(fù)雜度 。
??
六、第五范式
要求:
- 滿足1NF、2NF、3NF、4NF
- 如果將表中的多元關(guān)系分解一個(gè)一個(gè)的二元關(guān)系,一定會(huì)丟失信息
第五范式在4NF的基礎(chǔ)上, 進(jìn)一步消除依賴 。第五范式的要求明,如果不用這個(gè)表就不能正確說明數(shù)據(jù)之間的聯(lián)系。所以符合5NF的表已經(jīng)沒有任何多余依賴的存在了。所以第五范式是一個(gè)比較理想的范式。比如我們存儲(chǔ)玩家對(duì)戰(zhàn)和其發(fā)生地點(diǎn):
玩家1 |
玩家2 |
對(duì)戰(zhàn)地點(diǎn) |
Alice |
Lisa |
競(jìng)技場(chǎng)1 |
Alice |
Bob |
競(jìng)技場(chǎng)2 |
Bob |
Lisa |
競(jìng)技場(chǎng)1 |
現(xiàn)在,我們把它拆分成三個(gè)二元關(guān)系:
玩家1 |
玩家2 |
Alice |
Lisa |
Alice |
Bob |
Bob |
Lisa |
?
玩家1 |
對(duì)戰(zhàn)地點(diǎn) |
Alice |
競(jìng)技場(chǎng)1 |
Alice |
競(jìng)技場(chǎng)2 |
Bob |
競(jìng)技場(chǎng)1 |
?
玩家2 |
對(duì)戰(zhàn)地點(diǎn) |
Lisa |
競(jìng)技場(chǎng)1 |
Bob |
競(jìng)技場(chǎng)2 |
Lisa |
競(jìng)技場(chǎng)1 |
?
單獨(dú)看這三個(gè)子表,我們可以得出以下結(jié)論:
- Alice和Bob對(duì)戰(zhàn)過
- Alice在競(jìng)技場(chǎng)1和競(jìng)技場(chǎng)2都進(jìn)行過對(duì)戰(zhàn)
- Bob在競(jìng)技場(chǎng)1和競(jìng)技場(chǎng)2都進(jìn)行過對(duì)戰(zhàn)(結(jié)合第二、三個(gè)表)
?
從這三個(gè)獨(dú)立的結(jié)論,我們無法得知Alice和Bob究竟在那個(gè)競(jìng)技場(chǎng)進(jìn)行的對(duì)戰(zhàn),也就是發(fā)生了信息丟失。所以上邊那個(gè)表是符合5NF的。
?
好了,6個(gè)范式都看完啦,簡(jiǎn)單總結(jié)一下:
范式等級(jí) |
說明 |
1NF |
每一列都是原子項(xiàng),不可分割 |
2NF |
非主鍵屬性均完全依賴于主屬性,消除部分依賴 |
3NF |
所有非主鍵屬性之間沒有依賴關(guān)系,消除傳遞依賴 |
BCNF |
所有屬性均不傳遞依賴于任何候選鍵 |
4NF |
表中不包含超過一個(gè)多值屬性,消除多值依賴 |
5NF |
將表拆分為二元關(guān)系,一定會(huì)損失信息 |
?
?
感謝您看到這里!希望對(duì)您有一點(diǎn)幫助,歡迎批評(píng)和討論! ^_^
?
其他博客:
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
