從前面的討論可知,在分析過程常出現「多對多」的關係。若直接實作於資訊系統中,會出現資料不一致的問題發生。解決方法就是將個體進行正規化,也就是把個體變成資料表,而能實現於資料庫的過程。 正規化技術從1972年就開始研究成果了。發展至今,已經知道比較成熟的正規化技術有下列數種。但大部分的書籍或實務,都驗證到第三階正規化即可。
一般資訊系統都會非常複雜,很難用一個資料表就把所有狀況完整表達。一定是一堆資料表,彼此之間又受到業務規範的限制,發生不同的關聯。所以,ER-Diagram最後會呈現相當複雜結果。所設計的模型是否正確,實作上會不會出問題,最好的解決方式,就是採用正規化技術來驗證個體與屬性的關係。
前面提到,在建模過程中可分成Logical Mode與Physical Mode兩個階段與觀點。其定義分別如下:
簡單的說,ER-Diagram在Logical mode階段,主要是用來與使用者溝通的橋樑,整體瞭解資訊系統的作業流程下會有哪些資料結構,暫時不考慮會採用何種資料庫或其他資訊技術有關的部分。但到Physical mode階段就必須考量到資訊技術的限制因素。簡言之,模型設計的樣式會受到採用何種資料庫而有所影響。
從ER-Mode的關聯角度來看,Logical mode可以容許多對多的關聯存在,但是到了Phycial mode則無法接受。需要增加一個個體,將原本的一個多對多關係,拆解成兩個一對多的關係。到底要如何拆解呢,基本理論就是功能相依(Function Dependency),整個過程叫做正規化(Normalization)。
也可以這麼說吧,其實正規化的目的,就是要把ER-Model從Logical mode轉成Physical mode。一般習慣,在Logical mode稱為個體(Entity)、屬性(Attribute),到Physical modle就稱為資料表(Table)、欄位(Field)。
一個非最佳化的關聯式資料庫,在進行資料新增、更新或刪除的運算時,常導致資料庫異常,甚至會有資料消失的事情發生。因此,透過正規化技術來將個體間的關聯,進行最佳化的設計,以利後續資料庫的運作。
正規化技術的目的,就是合理解析個體內的屬性與主鍵值,讓所有屬性都必須要「功能相依」(Functional Dependency)於主鍵值。功能相依是處理正規化的基礎理論。
要注意,之前提到的關聯是指個體與個體之間的關係,而功能相依卻是在判斷屬性與屬性之間的關係。其定義為「有一個Entity叫R,假設擁有兩個屬性分別為A和B。如果A中的每一個值都恰與B的一個值相結合,我們稱”B is functional dependent on A”」。若以符號表示則為R.A → R.B,讀作“R.A功能決定R.B”。或者是說,”R.B被R.A所決定了”。在功能相依的式子中,在左端的給個專有名詞為決定集(determinant),在右邊的為相依集(dependent),兩者都是屬性的集合。
決定功能相依,常受到業務規範(Business Rule)的限制。在決定相依性時,務必把業務規範納進來。假設有個Suppliers-Parts的資料庫,假設供應商為S,零件為P。我們希望利用這個資料庫系統來得知「某個供應商提供某零件,目前的庫存量」。根據E-R Model的理論,此時有四種業務規範況狀發生,參見下表。
項次 | 狀況 | ER-Diagram |
---|---|---|
a | 一個供應商只能供應一種零件,且每種零件只能被一家供應商所供應。 | |
b | 一個供應商只能供應一種零件,但每種零件能被不同家供應商所供應。 | |
c | 每個供應商可以供應多種零件,但每種零件只能被一家供應商所供應。 | |
d | 每個供應商可以供應多種零件,而每種零件能被不同家供應商所供應。 |
根據企業實際運作而言,以上四種狀況都有可能發生。依據功能相依的理論,就會造成不同的E-R Diagram結果。因為功能相依是針對欄位間的關係,所以先賦予個體一些欄位定義,以方便說明。
以上的欄位似乎都沒有大太爭議,都可以經由主鍵值來取得相關的資料。 功能相依的表達式如下:
但是,若要問庫存量(QTY)時,也就是說此新增欄位(QTY),應該與誰相依才是正確的。因為,庫存量要被那個主鍵值所決定,會受業務規範所影響。再次把要解決的問題描述一遍,「某個供應商所提供的某零件,目前的庫存量是多少」,要回答這個問題,需個別考慮前述四種業務規範的狀況來決定。QTY到底會被那個主鍵值所決定,才能決定是要放在那個個體的屬性裡面,這樣才能夠找到正確的答案。所以,配合案例中的四種狀況,最後可能結果如下表。
狀況 | 表達式 | 說明 |
---|---|---|
a | S.S#→S.(Name,City,QTY) 或 P.P#→P.(Name,Color,QTY) |
因為是一對一,所以QTY放在S或者P都可以得知數量。 |
b | S.S#→S.(Name,City,QTY) | 因為供應商只能供應一種零件。 把QTY放在S中,就可以知道是此零件的庫存量。 |
c | P.P#→P.(Name,Color,QTY) | 此零件只被一家供應商所供應。 把QTY放在P中,可以知道是哪家供應商所提供的。 |
d | (S.S#,P.P#)→(QTY) | 因多對多關係,要知道哪個零件是哪一個供應商所提供的庫存量,就必須要給定兩個條件。 S.S#跟P.P#,這兩個複合鍵才能就確認實際的庫存量。 |
個體裡的每個屬性都必須經過功能相依的檢驗,方可確保未來資料處理上,如新增、刪除、查詢、修改時,不會發生資料遺漏或者不一致的現象。像d的狀況,為了要滿足功能相依的條件,就得多產生一個以(S.S#,P.P#)為主鍵值(複合鍵)的個體來存放QTY,像這種過程我們稱之為正規化。也就是說,一個條件成立的功能相依組合,就可以成為一個個體(Entity),也就是在Physical view 裡的資料表格。
雖然正規化的技術有很多,目前主要都只驗證到第三階正規化,一般是不會有大問題。本書只介紹到第三階正規化。
在討論正規化之前,應理解什麼是非正規化。首先來看,所謂非正規化的資料表會是如何。再強調一下,目前學習的範圍是在關聯式資料庫,要成為關聯式資料庫的資料表,還記得第三章所提到的,有資格稱為關聯式資料庫的資料表,其特性第一條為「該資料表(Table)的每一個格子(Cell)僅能包含一個值(Value)」。所以,非正規劃的資料表,基本上是不存在。本節所提到的資料表,並不符合關聯式資料庫的要求。
COBOL是一套商用語言,其資料處理能力,一直是程式語言中的翹楚。假設有一個叫PERSON的TABLE,在COBOL中的表示法為:
RECORD NAME IS PERSON.
02 EMPLOYEE# PICTURE “9(5)”.
02 EMPNAME PICTURE x(20).
02 SEX PICTURE x.
02 EMPJCODE PICTURE 9999.
02 BIRTH
03 MONTH PICTURE 99.
03 DAY PICTURE 99.
03 YEAR PICTURE 99.
02 NOSKILLS
02 SKILLS OCCURS NOSKILLS TIMES
03 SKILLCODE PICTURE 9999.
03 SKILLYEARS PICTURE 99.
在COBOL中SKILLS欄位,重複發生數次。記錄這個人所擁有的所有技術能力,且每個人可擁有不同種類的技術能力。此資料結構在COBOL中是非常容易表達呈現,但對關聯式資料庫而言,卻是一個很嚴重的問題。 首先要先決定到底要留多少欄位來存放SKILLS呢?因為每個人的技術能力項目都不一樣,預留太多欄位則浪費的記憶空間,留少了又無法完整表示這個人的技術能力。要克服這樣子的問題,就得靠正規化的技術了。接著就以一個訂單管理系統實例來解釋如何一步步進行正規化檢驗。
要稱之為第一階正規化,若且唯若所有的定義域都只包含純量值,沒有重複群組值。
以訂單管理為例,初步設計一個資料表。下圖很清楚違反資料表的基本要求,當然是違反了第一階正規化。因為Item欄位的資料不是純量。範例中,包含了許多項資料。
我們希望透過此資料表得到一些資訊。如找出一個訂單號碼(OrderID)後,我們可以知道是哪一位客戶(CustomerID)下的訂單,這還做得到。若接著想知道,賣出哪些貨品(Item)跟數量(Qty),就會有點困難了。若要問賣出的洗髮精是多少瓶,就不能利用這個資料表來得到答案,需要靠自己目視去算,才得知答案是14瓶。為何會這樣子呢,因為此資料表違反了第一階正規化。在Item的欄位中,含了重複的資料內容。
為了要滿足第一階正規化,要把資料表的結構作些改變,新的結構如下圖。
可是,這又回到COBOL老問題,到底要預留幾個Item與Qty才夠用呢?過多或過少都覺得不對。很多系統設計的人,沿襲著傳統設計檔案的概念在設計關聯式資料庫的資料表,所以才會造成系統彈性受到嚴格限制。若要解決這種問題,資料表的設計方式要再轉換成下圖所示,才能符合需求。
改成這種形式表達時,不管哪一張的訂單有甚麼樣變化,對系統而言都只是新增一筆記錄而已。現在我們想知道洗髮精到底賣了多少瓶,就可以很容易的做出來了。以下利用ACCESS的查詢功能來回答剛剛的問題。
執行所得如下圖,不用再靠目視了。
稱之為第二階正規化,必須已滿足第一階正規化。而且沒有任何一個非鍵值的屬性會功能相依於部分的主鍵值。
唸起來有點繞口,由實例來解釋會比較清楚。
為了讓訂單管理系統能更加完善,符合實際所需,需要增加一些欄位來收集必要的資訊,提升附加價值。修改後的資料表結構如下圖。
此資料表已滿足第一階正規化的要求,接著看是否滿足第二階正規化。第二階正規化的要求是沒有任何一個非鍵值的欄位,相依於部分主鍵值。也就是說,所有非鍵值的欄位,都必須相依於完整的主鍵值。滿足此要求者,才可以稱之為完成第二階正規化。
假設此資料表的主鍵值是(OrderID, OrderItem#),但是發現到CustomerID只要OrderID就可以被決定了。這樣子就不滿足第二階正規化的要求了。上述資料表,採用功能相依的理論來看,其表達式應可被拆成兩個。
也就是說,取得一個訂單編號(OrderID),就可以找出唯一的客戶編號(CustomerID)以及訂單日期(OrderDate)。相同的,給定了訂單標號(OrderID)以及項目編號(OrderIte#),就可以找出唯一的數量(Qty)、產品編號(ProductID)及產品名稱(Item)。為了讓每一個非主鍵值的欄位,都能夠功能相依於主鍵值,要將一個資料表分解成兩個資料表,其內容如下兩張圖。
分隔成兩個資料表,乍看下好像增加磁碟空間。實際上卻是大量減少磁碟空間。因為,原本在Order3的資料表中,CustomerID與OrderDate要出現9次,目前只要出現四次即可。
有了兩個資料表(Entity)就要注意其關聯性。目前這個資料表式一對多的關係,也就是說一張訂單會有多個訂單細項,但是一個訂單細項只能出現在某一訂單上面。用ACCESS來表示關聯如下圖。
稱之為地三階正規化,必須已經滿足第二階正規化。而且,非鍵值的屬性,都不是遞移的方式相依於主鍵值。
為了讓訂單管理系統達到最佳效率,必須對資料表進行第三階正規化的檢驗。舉一個狀況來說明為何經過第二階正規化後的表格,仍然會有問題的發生。假如有一天產品代號為4的香皂這項產品,最後決定要將名稱改為香精皂。如果要修正這個錯誤的話,對於只完成二階正規化的資料庫而言,要把資料表中產品屬性裡資料為香皂者,一筆筆找出來,並加以修改。資料表經過第三階正規化驗證過,資料具有高度一致性,任何一項修改將會是輕而易舉的事情。
第三階正規化簡單的來說,就是任何一個非主鍵值的欄位,一定要直接功能相依於主鍵值,不可以透過其他的值鍵來與主鍵值功能相依。 在Order5的資料表中,我們可以確定ProductID→Item,之後再與(OrderID, OrderItem#)功能相依。如此,就是違背了第三階正規化的檢驗。其實第三階正規化的主要目的,就是把一些資料重複性降到最低。
目前訂單管理系統已經逐漸邁向合理化的結構,經由第三階正規化的檢驗後,可確認需要三個資料表來建置訂單管理,如下圖。
三個資料表間的關聯性,採用ACCESS繪製E-R Diagram功能來表示,可得下圖。
經過三個正規化檢驗後,訂單管理系統的表格大致底定。未來可依據企業流程之需求或者報表需求,增加一些表格或欄位。所有這些新增之表格與欄位,一定要經過這三階正規化的檢驗,才能符合最佳化之需求。