規範化-數據庫設計原則

 

級別: 初級

, 浙江大學計算機科學與技術學院研究生
, IBM 上海軟件開發中心工具開發組的軟件工程師

2006 5 31

關係數據庫設計的核心問題是關係模型的設計。本文將結合具體的實例,介紹數據庫設計規範化的流程。

摘要

關係型數據庫是當前廣泛應用的數據庫類型,關係數據庫設計是對數據進行組織化和結構化的過程,核心問題是關係模型的設計。對於數據庫規模較小的情況,我們可以比較輕鬆的處理數據庫中的表結構。然而,隨着項目規模的不斷增長,相應的數據庫也變得更加複雜,關係模型表結構更爲龐雜,這時我們往往會發現我們寫出來的SQL語句的是很笨拙並且效率低下的。更糟糕的是,由於表結構定義的不合理,會導致在更新數據時造成數據的不完整。因此,就有必要學習和掌握數據庫的規範化流程,以指導我們更好的設計數據庫的表結構,減少冗餘的數據,藉此可以提高數據庫的存儲效率,數據完整性和可擴展性。本文將結合具體的實例,介紹數據庫規範化的流程。

序言

本文的目的就是通過詳細的實例來闡述規範化的數據庫設計原則。在DB2中,簡潔、結構明晰的表結構對數據庫的設計是相當重要的。規範化的表結構設計,在以後的數據維護中,不會發生插入(insert)、刪除(delete)和更新(update)時的異常。反之,數據庫表結構設計不合理,不僅會給數據庫的使用和維護帶來各種各樣的問題,而且可能存儲了大量不需要的冗餘信息,浪費系統資源。

要設計規範化的數據庫,就要求我們根據數據庫設計範式――也就是數據庫設計的規範原則來做。但是一些相關材料上提到的範式設計,往往是給出一大堆的公式,這給設計者的理解和運用造成了一定的困難。因此,本文將結合具體形象的例子,儘可能通俗化地描述三個範式,以及如何在實際工程中加以優化應用。

規範化

在設計和操作維護數據庫時,關鍵的步驟就是要確保數據正確地分佈到數據庫的表中。使用正確的數據結構,不僅便於對數據庫進行相應的存取操作,而且可以極大地簡化應用程序的其他內容(查詢、窗體、報表、代碼等)。正確進行表設計的正式名稱就是"數據庫規範化"。後面我們將通過實例來說明具體的規範化的工程。關於什麼是範式的定義,請參考附錄文章 1.

數據冗餘

數據應該儘可能少地冗餘,這意味着重複數據應該減少到最少。比如說,一個部門僱員的電話不應該被存儲在不同的表中, 因爲這裏的電話號碼是僱員的一個屬性。如果存在過多的冗餘數據,這就意味着要佔用了更多的物理空間,同時也對數據的維護和一致性檢查帶來了問題,當這個員工的電話號碼變化時,冗餘數據會導致對多個表的更新動作,如果有一個表不幸被忽略了,那麼就可能導致數據的不一致性。

規範化實例

爲了說明方便,我們在本文中將使用一個SAMPLE數據表,來一步一步分析規範化的過程。

首先,我們先來生成一個的最初始的表。

 

CREATE TABLE "SAMPLE" (

                   "PRJNUM" INTEGER NOT NULL,

                   "PRJNAME" VARCHAR(200),

                   "EMYNUM" INTEGER NOT NULL,

                   "EMYNAME" VARCHAR(200),

                   "SALCATEGORY" CHAR(1),

                   "SALPACKAGE" INTEGER)  

                  IN "USERSPACE1";

 

ALTER TABLE "SAMPLE"

         ADD PRIMARY KEY

                 ("PRJNUM", "EMYNUM");

 

Insert into SAMPLE(PRJNUM, PRJNAME, EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE)

values(100001, 'TPMS', 200001, 'Johnson', 'A', 2000), (100001, 'TPMS', 200002,

'Christine', 'B', 3000), (100001, 'TPMS', 200003, 'Kevin', 'C', 4000), (100002,

'TCT', 200001, 'Johnson', 'A', 2000), (100002, 'TCT', 200004, 'Apple', 'B',

3000);

 

 

 

1-1

 

 

考察表1-1,我們可以看到,這張表一共有六個字段,分析每個字段都有重複的值出現,也就是說,存在數據冗餘問題。這將潛在地造成數據操作(比如刪除、更新等操作)時的異常情況,因此,需要進行規範化。

第一範式

參照範式的定義,考察上表,我們發現,這張表已經滿足了第一範式的要求。

1、因爲這張表中字段都是單一屬性的,不可再分;

2、而且每一行的記錄都是沒有重複的;

3、存在主屬性,而且所有的屬性都是依賴於主屬性;

4、所有的主屬性都已經定義

事實上在當前所有的關係數據庫管理系統(DBMS)中,都已經在建表的時候強制滿足第一範式。因此,這張SAMPLE表已經是一張滿足第一範式要求的表。考察表1-1,我們首先要找出主鍵。可以看到,屬性對<Project Number, Employee Number>是主鍵,其他所有的屬性都依賴於該主鍵。

從一範式轉化到二範式

根據第二範式的定義,轉化爲二範式就是消除部分依賴。

考察表1-1,我們可以發現,非主屬性<Project Name>部分依賴於主鍵中的<Project Number>; 非主屬性<Employee Name><Salary Category><Salary package>都部分依賴於主鍵中的<Employee Number>

1-1的形式,存在着以下潛在問題:

1 數據冗餘:每一個字段都有值重複;

2 更新異常:比如<Project Name>字段的值,比如對值"TPMS"了修改,那麼就要一次更新該字段的多個值;

3 插入異常:如果新建了一個Project,名字爲TPT, 但是還沒有Employee加入,那麼<Employee Number>將會空缺,而該字段是主鍵的一部分,因此將無法插入記錄;

Insert into SAMPLE(PRJNUM, PRJNAME, EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE) values(100003, 'TPT', NULL, NULL, NULL, NULL)


4 刪除異常:如果一個員工 200003, Kevin 離職了,要將該員工的記錄從表中刪除,而此時相關的Salary信息 C 也將丟失, 因爲再沒有別的行紀錄下 Salary C的信息。

Delete from sample where EMYNUM = 200003
Select distinct SALCATEGORY, SALPACKAGE from SAMPLE

因此,我們需要將存在部分依賴關係的主屬性和非主屬性從滿足第一範式的表中分離出來,形成一張新的表,而新表和舊錶之間是一對多的關係。由此,我們得到:

 

CREATE TABLE "PROJECT" (

                   "PRJNUM" INTEGER NOT NULL,

                   "PRJNAME" VARCHAR(200))

                  IN "USERSPACE1";

 

ALTER TABLE "PROJECT"

         ADD PRIMARY KEY

                 ("PRJNUM");

 

Insert into PROJECT(PRJNUM, PRJNAME) values(100001, 'TPMS'), (100002, 'TCT');


1-2


1-3

CREATE TABLE "EMPLOYEE" (

                   "EMYNUM" INTEGER NOT NULL,

                   "EMYNAME" VARCHAR(200),

"SALCATEGORY" CHAR(1),

"SALPACKAGE" INTEGER)

                  IN "USERSPACE1";

 

ALTER TABLE "EMPLOYEE"

         ADD PRIMARY KEY

                 ("EMYNUM");

 

Insert into EMPLOYEE(EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE) values(200001,

'Johnson', 'A', 2000), (200002, 'Christine', 'B', 3000), (200003, 'Kevin', 'C',

4000), (200004, 'Apple', 'B', 3000);

 

Employee Number  Employee Name    Salary Category  Salary Package

200001   Johnson  A        2000

200002   Christine        B        3000

200003   Kevin    C        4000

200004   Apple    B        3000

 

CREATE TABLE "PRJ_EMY" (

                   "PRJNUM" INTEGER NOT NULL,

                   "EMYNUM" INTEGER NOT NULL)

                  IN "USERSPACE1";

 

ALTER TABLE "PRJ_EMY"

         ADD PRIMARY KEY

                 ("PRJNUM", "EMYNUM");

 

Insert into PRJ_EMY(PRJNUM, EMYNUM) values(100001, 200001), (100001, 200002),

(100001, 200003), (100002, 200001), (100002, 200004);

 

同時,我們把表1-1的主鍵,也就是表1-2和表1-3的各自的主鍵提取出來,單獨形成一張表,來表明表1-2和表1-3之間的關聯關係:

 

 

 

1-4

 

 

 

這時候我們仔細觀察一下表1-2, 1-3, 1-4, 我們發現插入異常已經不存在了,當我們引入一個新的項目 TPT 的時候,我們只需要向表1-2 中插入一條數據就可以了, 當有新人加入項目 TPT 的時候,我們需要向表1-3, 1-4 中各插入一條數據就可以了。雖然我們解決了一個大問題,但是仔細觀察我們還是發現有問題存在。

從二範式轉化到三範式

考察表前面生成的三張表,我們發現,表1-3存在傳遞依賴關係,即:關鍵字段< Employee Number > --> 非關鍵字段< Salary Category > -->非關鍵字段< Salary Package >。而這是不滿足三範式的規則的,存在以下的不足:

1 數據冗餘:<Salary Category><Salary Package>的值有重複;

2 更新異常:有重複的冗餘信息,修改時需要同時修改多條記錄,否則會出現數據不一致的情況;

3 刪除異常:同樣的,如果員工 200003 Kevin 離開了公司,會直接導致 Salary C 的信息的丟失。

Delete from EMPLOYEE where EMYNUM = 200003
Select distinct SALCATEGORY, SALPACKAGE from EMPLOYEE

因此,我們需要繼續進行規範化的過程,把表1-3拆開,我們得到:


1-5

 

1-6

 

這時候如果 200003 Kevin 離開公司,我們只需要從表 1-5 中刪除他就可以了, 存在於表1-6中的Salary C信息並不會丟失。但是我們要注意到除了表 1-5 中存在 Kevin 的信息之外, 1-4中也存在 Kevin 的信息, 這很容易理解, 因爲 Kevin 參與了項目 100001 TPMS 所以當然也要從中刪除。

至此,我們將表1-1經過規範化步驟,得到四張表,滿足了三範式的約束要求,數據冗餘、更新異常、插入異常和刪除異常。

在三範式之上,還存在着更爲嚴格約束的BC範式和四範式,但是這兩種形式在商業應用中很少用到,在絕大多數情況下,三範式已經滿足了數據庫表規範化的要求,有效地解決了數據冗餘和維護操作的異常問題。

結束語

在本文描述的過程中,我們通過結合實例的方法,通俗地演繹了數據表規範化的過程,並展示了在此過程中數據冗餘、數據庫操作異常等問題是如何得到解決的。

在具體的工程應用中,運用數據庫規範化的方法來設計數據庫表,將是具有現實意義的。

參考資料 

作者簡介

陳博,目前爲浙江大學計算機科學與技術學院研究生一年級在讀,碩士研究方向爲智能軟件工程,導師爲應晶教授。

蔣韜,目前是 IBM 上海軟件開發中心工具開發組的軟件工程師,主要負責基於 J2EE 構架的翻譯工具的開發和整合。2004 年畢業於復旦大學計算機系, 在進入 IBM 之前一直從事構件,構架整合和工具開發方面的工作,在 J2EE, DB2, XML, Web Services, IRUP, XP 方面都有豐富的項目經驗。

 

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