特別說明
資料庫的正規化是關係型資料庫理論的基礎。隨著資料庫的正規化工作的完成,資料庫中的各個資料表中的資料關係也就建立起來了。
在設計關係型資料庫時,最主要的一部分工作是將資料元素如何分配到各個關係資料表中。一旦完成了對這些資料元素的分類,對於資料的操作將依賴於這些資料表之間的關係,通過這些資料表之間的關係,就可以將這些資料通過某種有意義的方式聯絡在一起。例如,如果你不知道哪個使用者下了訂單,那麼單獨的訂單資訊是沒有任何用處的。但是,你沒有必要在同一個資料表中同時儲存顧客和訂單資訊。你可以在兩個關係資料表中分別儲存顧客資訊和訂單資訊,然後使用兩個資料表之間的關係,可以同時查看資料表中每個訂單以及其相關的客戶資訊。如果正規化的資料表是關係型資料庫的基礎的話,那麼這些資料表之間的關係則是建立這些基礎的基石。
出發點
下面的資料將要用在本文的例子中,用他們來說明如何定義資料庫表之間的關係。通過Boyce-Codd Normal Form(BCNF)對資料進行正規化後,產生了七個關係表:
Books: {Title*, ISBN, Price}
Authors: {FirstName*, LastName*}
ZIPCodes: {ZIPCode*}
Categories: {Category*, Description}
Publishers: {Publisher*}
States: {State*}
Cities: {City*}
現在所需要做的工作就是說明如何在這些表之間建立關係。
關聯類型
在家中,你與其他的成員一起存在著許多關係。例如,你和你的母親是有關係的,你只有一位母親,但是你母親可能會有好幾個孩子。你和你的兄弟姐妹是有關係的——你可能有很多兄弟和姐妹,同樣,他們也有很多兄弟和姐妹。如果你已經結婚了,你和你的配偶都有一個配偶——這是相互的——但是一次只能有一個。在資料表這一級,資料庫關係和上面所描述現象中的聯絡非常相似。有三種不同類型的關係:
一對一:在這種關係中,關係表的每一邊都只能存在一個記錄。每個資料表中的關鍵字在對應的關係表中只能存在一個記錄或者沒有對應的記錄。這種關係和一對配偶之間的關係非常相似——要麼你已經結婚,你和你的配偶只能有一個配偶,要麼你沒有結婚沒有配偶。大多數的一對一的關係都是某種商業規則約束的結果,而不是按照資料的自然屬性來得到的。如果沒有這些規則的約束,你通常可以把兩個資料表合并進一個資料表,而且不會打破任何正常化的規則。
一對多:主鍵資料表中只能含有一個記錄,而在其關係表中這條記錄可以與一個或者多個記錄相關,也可以沒有記錄與之相關。這種關係類似於你和你的父母之間的關係。你只有一位母親,但是你母親可以有幾個孩子。
多對多:兩個資料表裡的每條記錄都可以和另一個資料表裡任意數量的記錄(或者沒有記錄)相關。例如,如果你有多個兄弟姐妹,這對你的兄弟姐妹也是一樣(有多個兄弟姐妹),多對多這種關係需要引入第三個資料表,這種資料表稱為聯絡表或者串連表,因為關係型系統不能直接實現這種關係。
建立關係
在開始著手考慮建立關係表之間的關係之前,你可能需要對資料非常熟悉。只有在熟悉資料之後,關聯會比你剛開始的時候更明顯。你的資料庫系統依賴於在兩個資料表中找到的匹配值來建立關係。如果在資料庫系統中發現了一個匹配值,系統將從兩個資料表中提取資料並建立一個虛擬記錄。例如,你可能想要查看某個特定的作者所寫的全部書籍,在本文中,系統將從“Books”和“Authors”這兩個資料表中尋找相關的匹配值。需要注意的是,在大多數情況下,查詢的結果是動態,這意味著對這條虛擬記錄所做的任何改動都將可能作用到底層的資料表上,這一點是非常重要的。
進行匹配的值都是主鍵和外鍵的值。(關聯式模式不要求一個關係必須對應的使用一個主鍵來確定。你可以使用資料表中的任何備選關鍵字來建立關係,但是使用主鍵是大家都已經接受的標準。)主鍵(primary key)唯一的識別表中的每個記錄。而外鍵(foreign key)只是簡單的將一個資料表中的主鍵存放在另外一個資料表中。同樣地,對於你來說也不需要做太多的工作——只是簡單地將主鍵加到關係表中,並將其定義為外鍵。
唯一需要注意的是,外鍵欄位的資料類型必須和主鍵的資料類型相同。但是有些系統可以允許這條規則有一個例外,它允許在數字和自動編號(autonumbering)欄位(例如在SQL伺服器系統中訪問Identity和AutoNumber)之間建立關係。此外,外鍵的值可以是空(Null),儘管強烈建議在沒有特別原因的情況下,不要讓外鍵為空白。你有可能永遠都不會有機會來使用需要這項功能的資料庫。
現在回到我們的樣本關係表,並開始輸入合適的外鍵。(請繼續在紙上打草稿——在你的資料庫系統中建立真正的資料表還為時過早。要知道在紙上糾正錯誤要容易得多。)要記住,你正在把主鍵的值添加到關係表裡。只要調用實體之間的關係就行了,而其他的就簡單了:
書籍和分類是有關係的。
書籍和出版社是有關係的。
書籍和作者是有關係的。
作者和郵遞區號(ZIP)是有關係的。
郵遞區號和城市是有關係的。
城市和州是有關係的。
這一步並不是一成不變的,你可能會發現在正常化的過程中加入外鍵會更容易一些。在把欄位移動到一個新的資料表時,你可能要把這個新資料表的主鍵添加到原來的資料表裡,將其作為外鍵。但是,在你繼續正常化剩餘資料的時候,外鍵常常會發生改變。你會發現在所有這些資料表被全部正常化之後,一次添加所有的外鍵,這樣效率會更高。
操作資料表
現在讓我們一次操作一個資料表,就從Books資料表開始,它在這個時候只有三個欄位。很明顯,Authors、Categories和Publishers資料表的主鍵會被添加到Books裡。當你完成的時候,Books資料表就有了七個欄位:
Books
Title (PK)
ISBN (PK)
Price
FirstNameFK (FK) Authors.FirstName many-to-many
LastNameFK (FK) Authors.LastName many-to-many
CategoryFK (FK) Categories.Category many-to-many
PublisherFK (FK) Publishers.Publisher one-to-many
要記住,Authors資料表裡的主鍵是一個基于姓和名兩個欄位的複合關鍵字。所以你必須要把這個兩個欄位都添加到Books資料表裡。要注意,外鍵欄位名的結尾包含有FK這個尾碼。加入這個尾碼有助於提高可讀性和自我歸檔。通過名稱這種方式來區別外鍵會使得追蹤它們更簡單。如果主鍵和外鍵的名稱不同,這沒有關係。
這裡出現了三種關係:Books和Authors、Books和Categories,以及Books和Publishers。這三種關係中所存在的兩種問題可能沒有那麼明顯:
Books和Authors之間的關係:一本書可以有多個作者。
Books和Categories之間的關係:一本書可以被歸入多個類。
這兩者的關係是多對多的關係。先前我告訴過你,資料表不能直接實現這樣的關係,而需要第三個聯絡表來實現。(Books和Publishers的關係是一對多的關係,就像現在所說的,這樣是沒有問題的。)
這兩個新發現的多對多關係將需要一個聯絡表來包含來自每個資料表的主鍵,並將其作為外鍵。新的聯絡表是:
BooksAuthorsmmlink
TitleFK (FK) Books.Title one-to-many
ISBNFK (FK) Books.ISBN one-to-many
FirstNameFK (FK) Authors.FirstName one-to-many
LastNameFK (FK) Authors.LastName one-to-many
BooksCategoriesmmlink
TitleFK (FK) Books.Title one-to-many
ISBNFK (FK) Books.ISBN one-to-many
CategoryFK (FK) Categories.Category one-to-many
沒有必要更改Categories、Authors或者Publishers資料表。但是,你必須把FirstNameFK、LastNameFK和CategoryFK這三個外鍵從Books裡移走:
Books
Title (PK)
ISBN (PK)
Price
PublisherFK (FK) Publishers.Publisher one-to-many
現在,讓我們轉到Authors資料表上來,它現在有兩個欄位。每個作者都和ZIPCodes資料表中的郵遞區號的值相關。但是,每個郵遞區號會和多個作者相關。要實現這種一對多的關係,就要把ZIPCodes資料表中的主鍵添加進Authors資料表作為外鍵:
Authors
FirstName (PK)
LastName (PK)
ZIPCodeFK (FK) ZIPCodes.ZIPCode one-to-many
至此,你已經準備好了處理剩下的地址部分了。看到它們被分在不同的資料表裡是很讓人奇怪的,但是這是遵照BCNF正確正常化資料的結果。每個郵遞區號的值只會有一個對應的城市值和州值。每個城市和州的值只會被輸入進其對應的資料表裡一次。ZIPCodes和Cities資料表需要外鍵欄位來實現這些關係:
ZIPCodes
ZIPCode (PK)
CityFK (FK) Cities.City one-to-many
Cities
City (PK)
StateFK (FK) States.State one-to-many
States
State (PK)
從一個到九個
最後,你有了九個資料表:Books、Authors、Categories、Publishers、ZIPCodes、Cities、States、BooksAuthorsmmlink和BooksCategoriesmmlink。圖A是這個樣本資料表的資料庫最終的圖形形式。很難想像一個簡單的資料表會被分成九個資料表。
圖A
最初的一個資料表現在需要九個資料表了由於這個樣本資料庫很簡單,你可能會問這些關係有什麼作用。看起來仍在儲存冗餘的資料,只不過形式不同罷了——通過外鍵來實現。這是因為我們的資料表現在只有很少幾個欄位。試想一下有十幾個欄位的資料表,會是什麼樣的一個情形。需要承認的是,你仍然需要把資料表的主鍵作為外鍵儲存進關係表裡,但是至多可能最多增加一到兩個欄位。比較一下為這個資料表裡的每一條記錄都添加十幾個條目的情形吧。(