按照第三正規化的要求,是不應該存在冗餘欄位的,但有時候冗餘欄位可以提高效能,是可以使用的。
什麼是冗餘欄位?
在設計資料庫時,某一欄位屬於一個表,但它又同時出現在另一個或多個表,且完全等同於它在其本來所屬表的意義表示,那麼這個欄位就是一個冗餘欄位。
-------------------------------------------------------------
加深理解部分
在設計資料庫的時候,應該注意一下什麼呢?首先來看一張表。
學生成績表
姓名 | 性別 | java成績 | c成績 | 班級 |
張三 | 男 | 90 | 80 | 一班 |
李四 | 女 | 80 | 90 | 一班 |
楊過 | 男 | 90 | 80 | 一班 |
思考:上面這張表在設計上會出現什麼問題?
作為初學者看到這章表是回答不出什麼問題,其實從現實生活中來講,這的確沒什麼問題的,記得念中學的時候,每次考完試,心裡都很著急,急什麼呢?當然是成績,想想那個時候老師又是怎樣把你的成績資訊給列出來的呢?是不是以這樣的方式,每個班,甚至整個學校的學生成績都是使用一張表來列出來的,而我們每個人的成績利用該表中的一行資料來表示的,其中包括一個學生的學號,姓名,班級,成績以及排名等等。而這裡我們只是專業化成了資料庫,一個班級的學生成績變成了資料庫中的一張表,而每個學生的成績變成了表中的一條記錄而已,所以認為沒什麼問題。但是對資料庫的設計來講,概念就不一樣了,這樣設計就會很容易出現很多問題。會出現一下什麼問題呢。比如像增加異常,刪除異常,更新異常等等,下面是幾個例子。
eg1:在增加資料的時候,可能發生錯誤。這叫增加異常
姓名 | 性別 | java成績 | C成績 | 班級 |
張三 | 男 | 90 | 80 | 一班 |
李四 | 女 | 80 | 90 | 一班 |
楊過 | 男 | 90 | 80 | 一班 |
小龍女 | 女 | 100 | 70 | 1班 |
再仔細看上面這張表,在插入最後一條資料的時候,其中插入的班級和前面的不同,即“1班”,這從業務上來講,當然沒什麼問題,但是從資料庫的角度來講,確是兩個完全不同的概念,你可以認為是資料庫本身太笨了吧,所以這個時候插入的資料,會給我們帶來不必要的錯誤,這種錯叫做增加異常。
eg2:在刪除資料時,可能會刪除相關的其它資料,這叫刪除異常
這句話怎麼理解呢?同樣,看上面eg1的表,如果我們現在要指定刪除班級為“一班”的班級怎麼辦?這個時候我們使用delete 學生成績表 where 班級='一班' 的話,就刪除了班級='一班'的這些記錄,這個時候,不是也給我們帶來了不必要的後果嗎?這就是刪除異常。
eg3:在更新資料的時候,可能會有資料沒有更新到。這叫更新異常。
姓名 | 性別 | java成績 | C成績 | 班級 |
張三 | 男 | 90 | 80 | 1班 |
李四 | 女 | 80 | 90 | 1班 |
楊過 | 男 | 90 | 80 | 一班 |
那如何理解更新異常呢?先了解上面表在做什麼吧,上面表是在修改班級,我想把班級名稱全部改成“1班”,這個時候我會使用update語句來一條一條的修改,但是如果資料有很多很多的話,這個時候我們有可能忽略了某個地方,導致沒有修改到,所以在我執行某型操作的時候,就會出現資料與我們想象的不一致,這種結果就是所謂的更新異常導致的。上面舉了幾個例子,說明了在資料庫中是很容易出現問題的,而這些問題從業務上來講,我們很難查詢出來,那導致這樣的問題原因是什麼呢?有兩點:
1.看上面的表,把他們的資料全部耦合到一起了。
2.因為我這樣耦合的設計,導致產生了許多重複的資料,比如班級列,我就新增了三條一模一樣的,也就是一班。解決方法:根據實體和關係,我們可以把他們儲存在不同的表中,然後可以利用一個外來鍵,來指向我們所需要的資料。據上面例子,我們可以建立兩張表:
表1:
班級 | 班級編號 |
一班 | 001 |
二班 | 002 |
表2:
學號 | 姓名 | 班級編號 |
0103 | 張三 | 001 |
0104 | 李四 | 001 |
0201 | 楊過 | 002 |
0202 | 小龍女 | 002 |
這張表,就是把班級和學生資訊分開來儲存了,這個時候,班級和學生之間的關係是一對多,所以在資料庫設計的時候,我們可以將一對多中,多的那一方設定一個外來鍵列,這裡當然是學生是多的這方,所以我們在表中有一列叫做“班級編號”的外來鍵列,然後利用外來鍵列去引用我們的班級表中的“班級編號”列,從而建立起他們一件的那種一對多的關係。上面的問題是解決了,但是在以後我相信還會遇到相同的問題,也就是會遇到什麼增加,刪除或更新異常等,這就代表了以後的資料“難以維護”。所以我們要對資料庫進行適當的設計,才可以大大的增加可維護性。那麼進行資料庫設計,有六個正規化,一般說來,資料庫只需滿足第三正規化(3NF)就行了。什麼是正規化呢?為了將各種資訊進行有效的分類整理,就設計了一套行之有效的資料庫設計原則,這叫做正規化,英文normal forms,意思是規範化表。也表示將表進行分類處理,這個過程就叫做規範化。簡單理解,規範化表的過程,就是將資料進行分類的過程。下面說說這三個正規化。
1 第一正規化(1NF)
在任何一個關聯式資料庫中,第一正規化(1NF)是對關係模式的基本要求,不滿足第一正規化(1NF)的資料庫就不是關聯式資料庫。所謂第一正規化(1NF)是指資料庫表的每一列都是不可分割的基本資料項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重複的屬性。如果出現重複的屬性,就可能需要定義一個新的實體,新的實體由重複的屬性構成,新實體與原實體之間為一對多關係。在第一正規化(1NF)中表的每一行只包含一個例項的資訊。例如,對於員工資訊表,不能將員工資訊都放在一列中顯示,也不能將其中的兩列或多列在一列中顯示;員工資訊表的每一行只表示一個員工的資訊,一個員工的資訊在表中只出現一次。簡而言之,第一正規化就是無重複的行,並且列不可再分。
2 第二正規化(2NF)
第二正規化(2NF)是在第一正規化(1NF)的基礎上建立起來的,即滿足第二正規化(2NF)必須先滿足第一正規化(1NF)。第二正規化(2NF)要求資料庫表中的每個例項或行必須可以被惟一地區分。為實現區分通常需要為表加上一個列,以儲存各個例項的惟一標識。員工資訊表中加上了員工編號(emp_id)列,因為每個員工的員工編號是惟一的,因此每個員工可以被惟一區分。這個惟一屬性列被稱為主關鍵字或主鍵、主碼。第二正規化(2NF)要求實體的屬性完全依賴於主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性,如果存在,那麼這個屬性和主關鍵字的這一部分應該分離出來形成一個新的實體,新實體與原實體之間是一對多的關係。為實現區分通常需要為表加上一個列,以儲存各個例項的惟一標識。簡而言之,第二正規化就是非主鍵列必須依賴於主鍵列。第二正規化(2NF)值得注意的是:“NF2要求在資料庫設計時,每個表中的資訊,只能是自己實體所擁有的資訊。”
eg:屬性1必須依賴於屬性2:屬性1,一定是屬性2所代表的實體“天生”具有的。
學號 | 姓名 | 興趣1 | 興趣2 |
1 | a | 打球 | 打豆豆 |
2 | b | 開車 | 開玩笑 |
對這張表來說就是:姓名必須依賴於學號:姓名,一定是學號所代表的實體“天生”具有的如果想這張表不是屬於學號所代表的實體,即學生,那麼就應該將學生資訊和興趣分開來建表儲存。資料庫設計時,基本上都要滿足NF2.那什麼時候會違反NF2呢? 有一個需求,要求查詢:學號,姓名,班級名稱。由於學生姓名和班級名稱分開在2箇中,所以在查詢時,必須使用表連線。程式碼:select * from 班級表 c , 學生表 s where c.班級編號=s.班級編號。但是表連線會讓計算機做大量的匹配和計算,所以是比較消耗資源(資源主要是指CPU和記憶體)。為了提高效率,所以可以違反NF2,將表格如下:
班級表 | |
班級編號 | 班級名稱 |
學生表 | |||
學號 | 姓名 | 班級編號 | 班級姓名 |
現在查詢就只需要查詢一個表了,從而避免了表連線!學生表中的“班級名稱”是第2次出現,所以叫做“冗餘欄位”,這種設計方法叫做“逆正規化”!在本質上,實際上就是犧牲空間,換取時間。
3 第三正規化(3NF)
在滿足了第一正規化和第二正規化的基礎只上,可以滿足第三正規化,第三正規化簡而言之,就是非主鍵列之間是相對獨立的。先看一張表。
學號 | 姓名 | 出生日期 | 生肖 |
1 | 張三 | 1980-01-02 | 猴 |
2 | 李四 | 1983-04-02 | 牛 |
3 | 王五 | 1988-11-02 | 牛 |
生肖表
生肖編號 | 生肖名稱 |
1 | 猴 |
2 | 牛 |
這裡的生肖其實是和時間有關係了,可以通過時間來確定生肖是什麼,所以這裡的生肖完全可以忽略不計,一般這樣設計是為了減輕資料庫的計算壓力,所以才保留此欄位的,也就是前面提到的“冗餘欄位”,逆正規化。所以由此可以推出3NF是完全可以違反的。最後總結一下前三個正規化那些必須滿足:
NF1 行不可重複,列不可再分 - 必須滿足
NF2 非主鍵列必須依賴於主鍵列 - 必須滿足
NF3 非主鍵列之間必須相互獨立 - NF3可以不滿足