關于數(shù)據(jù)庫設計的總結(jié)
概覽
良好的數(shù)據(jù)庫設計不僅僅能夠滿足數(shù)據(jù)庫用戶的需求,而且對應用程序有著非常重大的影響。然而數(shù)據(jù)庫設計是一個復雜的過程,良好的數(shù)據(jù)庫設計并不是一件簡單的事。對于小型的應用,理解需求的數(shù)據(jù)庫設計者可能直接就能給出要構建的關系、關系的屬性以及其上的約束。但是現(xiàn)實的應用往往是復雜的,通常沒有一個人能夠理解應用所有的數(shù)據(jù)需求并直接給出最終的數(shù)據(jù)庫設計。因此遵循一個數(shù)據(jù)庫設計的方法是很有必要的。數(shù)據(jù)庫設計通常包括以下階段:
1. 完整的刻畫未來數(shù)據(jù)庫用戶的數(shù)據(jù)需求
2. 選擇數(shù)據(jù)模型,并采用所選數(shù)據(jù)模型的概念將需求轉(zhuǎn)化為數(shù)據(jù)庫的概念模式
3. 將抽象數(shù)據(jù)模型轉(zhuǎn)化為數(shù)據(jù)庫實現(xiàn):
- 邏輯設計:將高層概念模式映射到將使用的數(shù)據(jù)庫系統(tǒng)的實現(xiàn)數(shù)據(jù)庫模型
- 物理設計:指明數(shù)據(jù)庫的物理特征,包括文件組織格式和索引結(jié)構的選擇
本文將主要介紹如何構建一個數(shù)據(jù)模型,并將數(shù)據(jù)模型轉(zhuǎn)化為關系模式,以及如何評價關系模式的合理性。對于刻畫用戶的數(shù)據(jù)需求和物理設計并不會過多的介紹。因為數(shù)據(jù)需求來自于需求分析,這在軟件工程中是一個很大的過程;而物理設計和所選擇的 DBMS 有著很大的關系。
概念模型
實體-關系(E-R)數(shù)據(jù)模型是在數(shù)據(jù)庫最經(jīng)常使用的概念模型。因為它能夠?qū)F(xiàn)實世界的含義和交互映射到概念模式上,使得技術人員和非技術人員都能夠用統(tǒng)一的語言去描述用戶的數(shù)據(jù)需求。這一節(jié)首先將會介紹 E-R 模型,然后將說明如何將 E-R 模型轉(zhuǎn)化為關系模式。
E-R 模型介紹
E-R模型有三個基本概念:實體集、聯(lián)系集和屬性。這一小節(jié)首先會介紹這三個基本概念,然后將說明 E-R 模型上定義的一些約束。
實體集
實體是現(xiàn)實世界中可區(qū)別于所有其他對象的一個“事物”或者“對象”。比如公司里每個人都是一個實體。每個實體都有一些描述性性質(zhì)(被稱為屬性),其中一些性質(zhì)的可以唯一標識一個實體(被稱為碼)。比如工號將唯一標識一位員工。除了現(xiàn)實世界中實實在在的事物可以看作實體,一些抽象的事物也可以作為實體。比如購物訂單。實體集在 E-R 圖中使用分為兩部分的矩形表示,第一部分包含實體集的名字,第二部分包含實體集中的所有屬性,并且可以在唯一標識實體的屬性下面加上下劃線。
實體集是相同性質(zhì)的實體的集合。比如一個公司的所有員工的集合可以定義為實體集 employee。
有一些實體集本身找不出唯一標識實體集中單個實體的屬性,它必須依附于另一個實體才能存在,這種實體集叫做弱實體集。比如將 stackoverflow 上的答案作為一個實體,那么所有的答案就是一個弱實體集,因為每個答案都必須依附問題這個實體才能存在,它的唯一標識屬性是問題 ID 和答案 ID。與弱實體集相對應的就是那些本身的屬性就能唯一標識單個實體的強實體集。弱實體集在 E-R 圖中與強實體的表示類似,不同的是它的唯一標識屬性下面是虛下劃線。
聯(lián)系集
聯(lián)系是指多個實體間的相互關聯(lián)。比如一個項目和開發(fā)人員的聯(lián)系 develop,這一聯(lián)系指明這個項目是由哪些開發(fā)人員開發(fā)的。聯(lián)系也可以有描述性屬性。比如 develop 聯(lián)系可以增加 startsAt 屬性表明開發(fā)人員是哪天開始加入到這個項目的。
聯(lián)系集是相同類型聯(lián)系的集合。聯(lián)系集在 E-R 圖中使用菱形表示,而與弱實體集關聯(lián)的聯(lián)系集則使用雙菱形表示。聯(lián)系集的每個屬性都放在一個矩形中,通過虛線與聯(lián)系集相連接。
屬性
前面介紹實體集的時候介紹過屬性,這里將介紹屬性的分類:
- 簡單和復合屬性:不能劃分為更小的部分的屬性稱為簡單屬性,可以再分為更小的部分稱為復合屬性。比如一個 DBMS 的類型就是簡單屬性,而地址是一個復合屬性,它可以分為街道名、門牌號等。
- 單值和多值屬性:對于一個特定的實體,如果一個屬性只有一個值,就稱為單值屬性,否則為多值屬性。比如一個人的身份證號是單值的,但是他的手機號是多值的。
- 派生屬性:這類屬性的值可以從別的相關的屬性或者實體派生出來。比如一個人的信息有出生日期和年齡,那么年齡就是一個派生屬性。
約束
僅僅有實體和聯(lián)系并不能完全刻畫現(xiàn)實事物之間的關系,比如一個實體通過聯(lián)系集關聯(lián)到另一實體的個數(shù)、一個實體參與到聯(lián)系的個數(shù)。映射基數(shù)用來表示一個實體通過聯(lián)系集關聯(lián)到另一個實體的個數(shù),它必然是以下四種情況之一:
- 一對一:實體集 A 中的一個實體至多和實體集 B 中的一個實體相關聯(lián),反之亦然。
- 一對多:實體集 A 中的一個實體可以與實體集 B 中的任意數(shù)目相關聯(lián),而 B 中的一個實體至多與 A 中的一個實體相關聯(lián)。
- 多對一:實體集 A 中的一個實體至多和實體集 B 中的一個實體相關聯(lián),而 B 中的一個實體可以與 A 中的任意數(shù)目相關聯(lián)。
- 多對多:實體集 A 中的一個實體可以與實體集 B 中的任意數(shù)目相關聯(lián),B 中的一個實體也可以與 A 中的任意數(shù)目相關聯(lián)。
一個實體集參與到聯(lián)系的個數(shù)通過參與約束來描述。如果一個實體集 E 中的每個實體都參與到一個聯(lián)系集 R 的至少一個聯(lián)系中,則稱實體集 E 在聯(lián)系集 R 中是全部參與;如果 E 中只有部分實體參與到 R 的聯(lián)系中,則稱實體集 E 在聯(lián)系集 R 中是部分參與。
轉(zhuǎn)化為關系模式
E-R 模型是現(xiàn)實世界的含義和交互在概念模型上的體現(xiàn),而關系模式是數(shù)據(jù)庫中全體數(shù)據(jù)的邏輯結(jié)構和特征的描述。因此將 E-R 模型轉(zhuǎn)化為關系模式是一個里程碑式的階段,在這之后到最后的庫表結(jié)構就非常接近了。下面將介紹具體的轉(zhuǎn)化方式。
具有簡單屬性的強實體集的表示
具有簡單屬性的強實體集與其對應的關系模式的屬性是一一對應的,并且強實體集的主碼就是關系模式的主碼。比如一個公民的實體集有兩個屬性:身份證 ID、名字,那么對應的模式為:
- chinese_public(ID, name)
具有復雜屬性的強實體集的表示
對于具有復雜屬性的強實體集的轉(zhuǎn)化稍微復雜一點:
- 對于復合屬性自身并不直接創(chuàng)建一個屬性,而是將它所有的簡單屬性添加到關系模式中。
- 對于多值屬性,我們將創(chuàng)建一個新的關系模式。新的關系模式中的一個元組對應一個值,并且使用多值屬性所在的實體集的主碼進行關聯(lián)。
- 對應派生屬性,我們并不在關系模式中顯示的表達出來。
弱實體集的表示
弱實體集轉(zhuǎn)化為關系模式和強實體集類似,不同的是它的主碼包括它依賴的實體集的主碼和它自身的分辨符。
聯(lián)系集的表示
聯(lián)系集轉(zhuǎn)化而來的關系模式的屬性是它自身的屬性和參與到聯(lián)系的所有實體集的主碼的并集。關系模式的主碼選擇可以分為以下情況:
- 對于多對多的二元聯(lián)系,參與的實體集的主碼的并集成為主碼。
- 對于一對一的二元聯(lián)系集,任何一個實體集的主碼都可以選為主碼。
- 對于一對多或者多對多的二元聯(lián)系集,聯(lián)系集中“多”的那方實體集的主碼成為主碼。
- 對于 n 元聯(lián)系集,聯(lián)系集中非“一”的所有實體集的主碼的并集成為主碼。
在將聯(lián)系集轉(zhuǎn)化為關系模式時會出現(xiàn)關系模式的數(shù)量少于聯(lián)系集的數(shù)量的情況。這是因為模式的冗余和模式的合并的存在。
模式的冗余
考慮一個弱實體集,它本身就包括它所依賴的強實體集的主碼。如果這個弱實體集和它所依賴的強實體集的聯(lián)系集沒有其他屬性,那么所有出現(xiàn)在聯(lián)系集中屬性都將出現(xiàn)在弱實體集中。因此弱實體集轉(zhuǎn)化而來的關系模式是包括了聯(lián)系集轉(zhuǎn)化而來的關系模式的所有屬性。這種情況下,不需要為聯(lián)系集給出對應的關系模式。
模式的合并
考慮實體集 A 到實體集 B 的一個多對一的聯(lián)系集 AB。按照前面的方法,我們將得到三個關系模式:A、B 和 AB。那么我們可以將 A 和 AB 模式合并成包含兩個模式的所有屬性的并集的模式,并且合并后模式的主碼就是 A 的主碼。如果 A 是全部參與的,那么合并后模式中來自 B 的屬性都是有值的;否則 A 中未參與聯(lián)系集的元組在合并后模式對應的元組中,來自 B 的屬性是 NULL。對于一對一的聯(lián)系集,它的關系模式可以合并到任意一個實體集中。
規(guī)范化
前面介紹了 E-R 模型以及如何將 E-R 模型轉(zhuǎn)化為關系模式,但是得到的關系模式就是一個好的設計嗎?答案顯然是不一定的,如果 E-R 模型本身質(zhì)量就不高,那么得到關系模式大概率質(zhì)量也是不高的。要回答這個問題,首先需要明確什么樣的設計是好的或者是不好的,然后才能做出評價。對于不好的設計,我們需要給出一種方法將它變成一個好的設計。
一個不好的設計
這是一個圖書館圖書當前出借的表,所有的信息都保存在這張表中。

這張表存在什么樣的問題呢?首先是數(shù)據(jù)冗余的問題。如果一個人多次借了多本書,那么這個人的信息會多次重復;如果一本書被多個人借過,那么這本書的信息也會多次重復。而數(shù)據(jù)冗余會帶來數(shù)據(jù)一致性的問題。修改一個人的信息需要更新他的所有借書記錄;修改一本書的信息同樣需要更新所有包括這本書的記錄。其次是數(shù)據(jù)完整性的問題。一個圖書館的會員如果沒有借過書,那么將無法保存他的信息;一本新書如果沒有被借過,那么這本書的信息也將無法保存;如果一個人注銷自己的賬號,而有一本書只有他借過,那么這本書的信息也將隨之消失。
應用范式進行規(guī)范化
前面通過一個例子說明了不好的設計會有數(shù)據(jù)冗余和完整性的問題。下面將通過范式將其規(guī)范化,以消除這些問題。
第一范式
第一范式要求每個列的值域都是由原子值組成,每個字段的值都只能是單一值,并且每一行需要有主鍵。
以前面的例子為例,為了滿足第一范式,我們需要將出借的圖書和圖書類別的多個值放到不同的行中,并且將 ID 和出借的圖書作為主鍵(假設圖書名不會重復)。下面是修改后的結(jié)果:

顯然,第一范式只能讓表看起來更好看一些,對于前面的問題并沒有實質(zhì)性的解決。所以,接下來需要增加約束。
第二范式
第二范式的要求有兩點:
- 滿足第一范式
- 非主屬性對于所有主屬性完全函數(shù)依賴
這里需要解釋幾個名詞:
- 非主屬性:不包含在主鍵中的屬性。
- 主屬性:包含在主鍵中的屬性。
- 完全函數(shù)依賴:若屬性集 X 和屬性集 Y 之間存在函數(shù)關系 X -> Y,且對于X的任何一個真子集 X‘,X‘ -> Y 不成立,那么我們稱 Y 對于 X 完全函數(shù)依賴。
也許有人會覺得完全函數(shù)依賴的解釋看起來和沒解釋是一樣的,所以這里使用上面的例子進行說明。上面提到過,我們使用 ID 和出借的圖書作為主鍵,那么 ID 和出借的圖書一旦確定,名字、居住地也隨之確定,即函數(shù)(ID, 出借的圖書) -> (名字, 居住地)成立。但是,如果 ID 確定了,名字、居住地也能被確定下來,所以對于(ID, 出借的圖書)的真子集(ID),函數(shù)(ID) -> (名字, 居住地)依然成立。所以上面的表不滿足第二范式。
我們需要對這張表進行拆分為會員表和圖書表以及圖書當前出借表:



第三范式
從上面的例子來看,通過第二范式的改造,之前提到的問題已經(jīng)得到了解決。那么為什么又要有第三范式呢?首先我們來看一個滿足第二范式但是仍然存在前面問題的例子。

這個關系模式的主鍵是圖書名,顯然它滿足第一范式。因為主鍵只有一個字段,非主屬性對于所有主屬性一定是完全函數(shù)依賴,所以它也滿足第二范式。但是如果在給這張表增加一本人民郵電出版社出版的《深入理解 MySQL》,我們就會發(fā)現(xiàn)出版商的地址會存在冗余!為了消除這種冗余,第三范式就被提出來了。
第三范式需要滿足以下兩點:
- 滿足第二范式
- 非主屬性對于主碼不存在傳遞函數(shù)依賴
什么是傳遞函數(shù)依賴呢?這里不給形式化的定義,而以前面的例子來解釋。前面的例子中因為圖書名是主鍵,所以函數(shù)(圖書名) -> (出版商)是成立的。同時我們知道出版商的地址是確定的,所以函數(shù)(出版商) -> (出版商地址)也是成立的。所以就有(圖書名) -> (出版商) -> (出版商地址)成立。這個時候我們也稱出版商地址函數(shù)依賴于圖書名。
為了消除非主屬性對于主碼的傳遞函數(shù)依賴,我們將上面的表拆分為兩個表:


BCNF
前面提到的第二范式和第三范式分別消除了非主屬性對于主屬性的部分函數(shù)依賴和傳遞依賴,那么如果主屬性對于主鍵存在部分函數(shù)依賴和傳遞函數(shù)依賴會怎么樣呢?舉個例子,假設:
- 一個數(shù)據(jù)庫公司給多個甲方提供技術支持
- 一個甲方公司現(xiàn)場只需要一名技術支持人員,一名技術支持人員也只能在一個甲方公司做支持
- 一個甲方公司使用了數(shù)據(jù)庫公司的多個產(chǎn)品,每個產(chǎn)品的維護時間都不一樣
那么考慮關系模式 現(xiàn)場支持(甲方公司,技術支持人員,項目名,支持時間),它的主鍵是甲方公司、技術支持人員、項目名。

它是滿足第三范式的,因為不存在非主屬性對于主鍵的部分函數(shù)依賴和傳遞依賴。但是它仍然是有問題的:如果技術支持人員被調(diào)到另一家甲方公司,那么該技術人員關聯(lián)的所有記錄都要修改;同時,如果一個甲方公司和數(shù)據(jù)庫公司有多個項目,那么甲方公司和技術支持人員都需要重復。所以,滿足第三范式的關系模式并不一定能夠完全解決前面的問題。針對這個問題,BCNF 就被提出來了,這里給出它的要求:
- 滿足第三范式
- 主屬性對于主鍵不存在部分函數(shù)依賴和傳遞依賴
上面的例子主屬性(甲方公司)部分依賴于主鍵(技術支持人員,項目名)。修改之后的關系模式為:


小結(jié)
回顧 E-R 關系模型和范式,其實我們可以發(fā)現(xiàn):
- 不滿足第一范式的關系模式可能是根本沒有建立 E-R 模型,直接將所有信息放到一張表;或者是 E-R 模型沒有主鍵;或者是 E-R 模型轉(zhuǎn)化為關系模式時多值屬性、復合屬性沒有處理好
- 不滿足其他范式比較大的原因應該是沒有正確的識別實體集和關系集。比如第二范式中的例子,會員實體集和圖書實體集的信息完全混到了一起;第三范式中圖書實體集和出版商實體集混到了一起;BCNF 例子則更可能來自實體集甲方公司、技術支持人員、項目的一個三元關系集。
進一步地,如果 E-R 模型的質(zhì)量高,那么得到的關系模式滿足的更高等級的范式的可能性也會大很多。本人工作經(jīng)驗有限,并沒有實際經(jīng)歷完整的建模、規(guī)范化,只是從有經(jīng)驗的人了解到大多數(shù)業(yè)務公司會做的是建模,而規(guī)范化比較少。這大概是他們的模型都建立的很好吧(:。當然,實際設計數(shù)據(jù)庫時也不一定要滿足范式,比如有時為了業(yè)務的方便,也會選擇部分數(shù)據(jù)的冗余。
總結(jié)
本文只介紹了數(shù)據(jù)庫設計一小部分,這一小部分對于一個高質(zhì)量的數(shù)據(jù)庫設計是很重要的,但是也是遠遠不夠的。為了得到一個高質(zhì)量的數(shù)據(jù)庫設計,還需要從兩方面去努力。一方面,在 E-R 模型的上游需要做好領域模型的構建,以便于對需要構建的系統(tǒng)有更深入的理解,從而得到更高質(zhì)量的 E-R 模型。另一個方面,需要非常了解使用的 DBMS 的特性。比如使用 MySQL,需要知道使用哪種類型的存儲引擎存儲是合適的,使用什么類型的字段是合適的等;有的人還建議不使用關系模式的主鍵,而是使用自增主鍵。這兩方面也不是完全孤立的,比如要為哪些字段創(chuàng)建索引要基于查詢來考慮,而最經(jīng)常會有的查詢其實在業(yè)務建模的時候就能明確的??偟膩碚f,數(shù)據(jù)庫設計是一個系統(tǒng)的工程,需要對整個系統(tǒng)都有詳細的了解才能做好。