理解資料庫的幾種鍵和幾個正規化
在上學的時候,資料庫是一門讓我比較頭大的課程。記得當時教材上淨是一些晦澀難懂的語言,沒有充足的例項來幫助理解。前一陣子在看《網路遊戲伺服器端程式設計》的過程中,突然對資料庫正規化有了一些感覺,在此總結一下,分享給大家。作者純菜鳥,即使總結這些基礎知識也難免有錯,希望給位大牛不吝賜教,謝謝!
鍵(關係鍵)以及資料庫正規化都是關係資料庫的概念。所謂關係鍵,指的是一個表中的一個(或一組)屬性,用來標識該表的每一行或與另一個表產生聯絡。
資料庫的”正規化“,指的是設計資料庫的規則。按照一定的規則設計出資料庫的表和關係,能夠避免在一些情況下的查詢出錯,並具有良好的結構。總的來說,隨著正規化等級的提高,資料表屬性之間的依賴關係越來越小,資料冗餘越來越低。但同時,資料關係變得更加複雜,訪問一個具體資料的關係層次增加。
我們先來看一下幾種常見的資料庫關係鍵:
1、超鍵(super key):能夠唯一標識一條記錄的屬性或屬性集。
-
- 標識性:一個數據表的所有記錄都具有不同的超鍵
- 非空性:不能為空
2、候選鍵(candidate key):能夠唯一標識一條記錄的最小屬性集
-
- 標識性:一個數據表的所有記錄都具有不同的候選鍵
- 最小性:候選鍵的任何子集都不能唯一標識一個記錄
- 非空性:不能為空
- 候選鍵是沒有多餘屬性的超鍵
3、主鍵(主碼、primary key):某個能夠唯一標識一條記錄的最小屬性集
-
- 唯一性:一個數據表只能有一個主鍵
- 標識性:一個數據表的所有記錄都具有不同的主鍵取值
- 非空性:不能為空
- 選取某個候選鍵為主鍵
4、外來鍵(foreign key):子資料表中出現的父資料表的主鍵,稱為子資料表的外來鍵。
5、代理鍵:當不適合用任何一個候選鍵作為主鍵時(如資料太長等),新增一個沒有實際意義的鍵作為主鍵,這個鍵就是代理鍵。(如常用的序號1、2、3)
6、自然鍵:自然生活中唯一能夠標識一條記錄的鍵(如身份證)
下面就來看一下常見的幾種關係資料庫正規化吧。
一、第一正規化(1NF)
要求:
- 每一個屬性都不能再分割,都是原子項。
第一正規化是關係型資料表的基本要求,但是如何判斷一個屬效能否再分割呢?這沒有統一的標準,需要依照需求確定
編號 |
怪物名 |
掉落金錢 |
1 |
巨熊 |
100 |
這個表格看上去並沒有什麼問題。每一個屬性項都是”不可分割“的,所以符合第一正規化。但是,如果我們希望把玩家擊殺怪物之後獲得的金錢分成兩部分,一部分是固定收益,另一部分是一個隨機的浮動收益(比如和玩家幸運值有關)。則這張表格中的”掉落金錢“項就不是”不可分割“了,也就不符合第一正規化了。如果有這種需求,我們就可以把”掉落金錢“分割為”固定金錢“和”浮動金錢“兩部分。如下所示:
編號 |
怪物名 |
固定金錢 |
浮動金錢 |
1 |
巨熊 |
80 |
20 |
這樣分割之後,使得每一項都不能再分割,從而使得資料表滿足第一正規化。
滿足第一正規化的資料表有什麼好處呢?
- 1NF保證了資料庫的每一列都是不同的。每一列的資料彼此沒有任何交集。
- 這樣做首先減少了資料的冗餘,節省儲存空間。如果不滿足第一正規化,一些資料項有可能包含相同的”子項“,造成儲存空間的浪費。
- 其次,每一列沒有重複的資料意味著不需要考慮資料更新的同步問題。不用擔心在一列中更新了資料,還要在另一列做相應修改。
- 另外,每一列的資料不可再分,在某些情況下減少了資料訪問的層數,提高資料訪問速度。
二、第二正規化(2NF)
要求:
- 滿足第一正規化
- 非主鍵屬性均完全依賴於主鍵
非主鍵屬性和主鍵可以有什麼關係?1、完全依賴。2、部分依賴。3、不依賴(沒關係)。顯然第三種情況下,這個屬性就不應該放在這張資料表中。所以2NF要求非主鍵屬性完全依賴於主鍵,就是在消除非主鍵屬性對主鍵的部分函式依賴。既然是部分函式依賴,暗含著說主鍵是一個複合鍵(由多個屬性組成的鍵)。如果某個非主鍵屬性只和主鍵中的一部分有關(部分函式依賴),則不符合第二正規化。舉例,網路遊戲的使用者資料表:
玩家使用者名稱 |
角色名 |
角色職業 |
上次登入時間 |
Alice |
superman |
wizard |
2013-11-4 |
如果我們的遊戲允許一個玩家擁有多個角色,則在這張表中“玩家使用者名稱”和“角色名”構成複合主鍵,唯一標識一條記錄。表中的“角色職業”,與玩家使用者名稱和角色名均相關,為完全依賴於主鍵。而“上次登入時間”僅和“玩家使用者名稱”相關,而與角色名無關。所以“上次登入時間”部分函式依賴於主鍵。本關係不符合2NF。
要將上表轉換為符合2NF的結構也很簡單,只要把部分函式依賴的部分抽出來,組成新的表即可。如下所示:
玩家使用者名稱 |
角色名 |
角色職業 |
Alice |
superman |
wizard |
玩家使用者名稱 |
上次登入時間 |
Alice |
2013-11-4 |
符合2NF能給我們帶來什麼好處呢?2NF消除了屬性對主鍵的部分函式依賴。
首先,2NF可以在一定程度上消除冗餘,節省儲存空間。
如果存在部分函式依賴,則可能存在資料冗餘。在多條記錄中,主鍵中的某一個屬性可能是一樣的,而如果有其他資料項函式依賴於這個不變的屬性,則這些資料項也將是一樣的。比如在上面例子中,在修改之前的表中,如果有多個角色名對應一個玩家使用者名稱,則會有多條資料。它們具有一樣的使用者名稱和不同的角色名。由於上次登入時間僅依賴於玩家使用者名稱,所以在這多條記錄中,上次登入時間也都是相同的,造成了冗餘。
其次,2NF簡化了表的邏輯關係,使得表的結構更加清晰。
三、第三正規化(3NF)
要求:
- 滿足第一、二正規化
- 所有非主鍵屬性之間沒有函式依賴關係
3NF在2NF的基礎上,進一步消除非主鍵屬性之間的函式依賴關係。實質上,也是消除非主鍵屬性中的傳遞依賴。更進一步地說,如果兩個資料表有關係。那麼這兩個資料表中的非主鍵屬性必須是不同的。如果存在一個非主鍵屬性A,存在於兩張表中。則在某張表中,A依賴於外來鍵,從而不符合3NF。比如網路遊戲中拍賣行的資料,可以按照下面的表格進行儲存:
玩家姓名 |
物品名 |
單價 |
數量 |
總金額 |
Alice |
治療藥劑 |
50 |
10 |
500 |
在這個表格中,“總金額”項可以通過“單價”和“數量”運算得出,存在函式依賴關係,不滿足3NF。
要將這個表格修改為滿足3NF的要求,只需要從表中刪除“總金額”即可。在另外一些情況中,可以將函式依賴關係涉及到的項單獨抽出來組成新的表,需要具體情況具體分析。
3NF的優點很明顯,可以減少資料冗餘,節省儲存空間。既然存在函式依賴,某些資料項就能夠通過其他資料項計算得出,很可能存在資料冗餘。值得注意的是,在一些情況下,存在這種資料冗餘的表格是有意義的。如果在表格中儲存著某些運算的結果,我們在使用這些結果時就不用進行運算了,節省了運算時間,是一種“空間換時間”的做法。從這裡也可以看出,應用正規化並不能夠保證最好的效果,需要根據應用需求進行合理取捨。
四、BC正規化(boyce-codd正規化,BCNF)
要求:
- 滿足1NF、2NF、3NF
- 所有屬性(包含主鍵屬性和非鍵主屬性)都不傳遞依賴於任何候選鍵
BC正規化在3NF的基礎上,要求主鍵屬性也不能傳遞依賴於任何候選鍵。當主鍵是複合鍵是,主鍵的某個屬性可能會依賴於某個候選鍵。此時,關係能夠符合3NF,因為並不是“非主鍵”屬性依賴於某個非主鍵屬性。但此關係並不符合BC正規化。例如,在以房間為組織方式的遊戲中,我們記錄某個玩家、房間和房主的關係。
房主ID |
房間ID |
玩家ID |
Alice |
123 |
Bob |
表中的依賴關係有:
- (玩家ID,房間ID)-> 房主ID
- 房主ID -> 房間ID
- (玩家ID,房主ID)-> 房間ID
同時,表中的候選鍵有(玩家ID,房間ID)、(玩家ID,房主ID)。比如,我們選擇主鍵為(玩家ID,房間ID),那麼,房間ID就是主鍵的一個屬性。而在依賴關係2中,房間ID依賴於房主ID,房主ID是候選鍵(玩家ID,房主ID)的一個屬性。那麼,首先,由於房間ID不是候選鍵屬性,所以此表並沒有違反3NF。但是由於房間ID和房主ID存在依賴關係,所以滿足“主鍵屬性傳遞依賴於某個候選鍵”的條件,所以此表不符合BC正規化。
要把上表修改為滿足BC正規化的形式,只要把它進行合理拆分即可。
房間ID |
玩家ID |
123 |
Bob |
房間ID |
房主ID |
123 |
Alice |
BC正規化的好處是進一步消除了表中的依賴關係,減少了冗餘。例如在上例中,如果我們採用未修改的版本,如果想要儲存一個10個玩家(不含房主)的房間,就需要10條這樣的記錄才可以。
五、第四正規化
要求:
- 滿足1NF、2NF、3NF
- 表中不能包含一個實體的兩個或多個多值屬性
所謂多值屬性,指的是某個屬性可以包含多個值。這個屬性的(多個)取值,被另一個屬性決定。也就是說,一旦確定了某個屬性,另一個屬性的多個取值就一起確定了。第四正規化在第三正規化的基礎上,消除多值依賴。所謂多值依賴,指的是一組值(多值屬性)依賴於另一個屬性。函式依賴是一對一的關係,多值依賴是一對多的關係。這個理解起來我感覺有點彆扭,可能我的理解也有偏差,說出來和大家一起探討一下。
比如,我們要在資料庫中儲存玩家的角色技能資訊,這裡我們允許一個玩傢俱有多個角色,一個角色具有多個技能:
玩家ID |
角色名 |
技能 |
Alice |
superman |
Fire ball |
首先,這個表只有一個候選鍵(玩家ID、角色名、技能)。所以肯定符合3NF。進一步觀察一下,玩家ID是一個單值屬性。角色名就是一個多值屬性了,因為一個玩家ID可以對應多個角色名。角色名在表中看起來是一項,這是由於受制於具體資料庫提供的功能。邏輯上,我們拿到一個玩家ID,可以確定的是,這個玩傢俱有某些角色,是一個一對多的關係,是多值依賴。角色名是一個多值屬性。同樣的,一個角色也對應著多個技能,這也是多值依賴。技能也是一個多值屬性。顯然,這個表並不符合4NF。
這個表有什麼問題呢?
首先,資料冗餘大,如果一個玩家有好幾個具有Fire Ball技能的角色,這個技能項就要重複儲存幾次。
其次,增、刪、改都比較複雜,比如我們要刪除Fire Ball技能,那麼,我們要刪除這個玩家所有具有Fire Ball技能的表項。
要將上表修改為符合4NF的表,只需要將多值依賴進行合理對映即可:
玩家ID |
角色名 |
Alice |
superman |
角色名 |
技能 |
superman |
Fire ball |
這兩個表都符合4NF。
可以看出,4NF的使用可以降低資料冗餘,並且減少資料處理複雜度。
六、第五正規化
要求:
- 滿足1NF、2NF、3NF、4NF
- 如果將表中的多元關係分解一個一個的二元關係,一定會丟失資訊
第五正規化在4NF的基礎上,進一步消除依賴。第五正規化的要求明,如果不用這個表就不能正確說明資料之間的聯絡。所以符合5NF的表已經沒有任何多餘依賴的存在了。所以第五正規化是一個比較理想的正規化。比如我們儲存玩家對戰和其發生地點:
玩家1 |
玩家2 |
對戰地點 |
Alice |
Lisa |
競技場1 |
Alice |
Bob |
競技場2 |
Bob |
Lisa |
競技場1 |
現在,我們把它拆分成三個二元關係:
玩家1 |
玩家2 |
Alice |
Lisa |
Alice |
Bob |
Bob |
Lisa |
玩家1 |
對戰地點 |
Alice |
競技場1 |
Alice |
競技場2 |
Bob |
競技場1 |
玩家2 |
對戰地點 |
Lisa |
競技場1 |
Bob |
競技場2 |
Lisa |
競技場1 |
單獨看這三個子表,我們可以得出以下結論:
- Alice和Bob對戰過
- Alice在競技場1和競技場2都進行過對戰
- Bob在競技場1和競技場2都進行過對戰(結合第二、三個表)
從這三個獨立的結論,我們無法得知Alice和Bob究竟在那個競技場進行的對戰,也就是發生了資訊丟失。所以上邊那個表是符合5NF的。
好了,6個正規化都看完啦,簡單總結一下:
正規化等級 |
說明 |
1NF |
每一列都是原子項,不可分割 |
2NF |
非主鍵屬性均完全依賴於主屬性,消除部分依賴 |
3NF |
所有非主鍵屬性之間沒有依賴關係,消除傳遞依賴 |
BCNF |
所有屬性均不傳遞依賴於任何候選鍵 |
4NF |
表中不包含超過一個多值屬性,消除多值依賴 |
5NF |
將表拆分為二元關係,一定會損失資訊 |
相關推薦
理解資料庫的幾種鍵和幾個正規化
在上學的時候,資料庫是一門讓我比較頭大的課程。記得當時教材上淨是一些晦澀難懂的語言,沒有充足的例項來幫助理解。前一陣子在看《網路遊戲伺服器端程式設計》的過程中,突然對資料庫正規化有了一些感覺,在此總結一下,分享給大家。作者純菜鳥,即使總結這些基礎知識也難免有錯,希望給位大牛不吝賜教,謝謝! 鍵(
XMLHttpRequest物件的幾種狀態和幾個重要屬性以及常用的方法
XMLHttpRequest的幾種狀態: 0: 物件沒有完成初始化 1: 物件開始傳送請求 2: 物件的請求傳送完成 3: 物件開始讀取伺服器響應 4: 物件讀取伺服器響應結束 1.onreadystatechange 指定XML
如何在網頁中禁止使用滑鼠右鍵?幾種方法和大家分享
如何在網頁中禁止使用滑鼠右鍵 方法一:最簡單的方法<SCRIPT language="JavaScript"> function click() { if(event.button==2
Java專案中連線資料庫的幾種方式和範例
1、最原始的寫法(也是一般初學者的寫法) 首先import資料庫連線基礎類,然後其它的然後就簡單了。實際操作過程中只需載入驅動程式類,之後呼叫sql語句就行了。以下是一個簡單的程式例子。 //Select.java import java.net.URL; im
【轉載】關於shutdown halt reboot poweroff init 0幾種關機和重啟命令的理解
shutdown 先執行關閉程序,寫入磁碟等一系列操作後 init 0關機 init 6 重啟halt 沒有在init 0 ,init 6執行級別時,呼叫shutdown(相當於shutdown -h now)reboot 沒有在init 0 ,init 6執行級別時,
利用JS提交表單的幾種方法和驗證(必看篇)
www contain 功能 ner ble 四種 利用 comm pac 第一種方式:表單提交,在form標簽中增加onsubmit事件來判斷表單提交是否成功 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Spring bean初始化與銷毀的幾種方式和區別
pack ack 構造 rop struct service() throws esc println 1. <bean> 元素的 init-method/destroy-method屬性指定初始化之後 /銷毀之前調用的操作方法 2. 指定方法上加上@PostC
清除浮動的幾種方式和兼容性處理
不容易 初學 str strong ott tex right pos 文檔流 清除浮動的幾種方式和兼容性處理 在清除浮動前我們要了解兩個重要的定義: 浮動的定義:使元素脫離文檔流,按照指定方向發生移動,遇到父級邊界或者相鄰的浮動元素停了下來。 高度塌陷:浮動元素父元素高度
獲得元素的幾種方法,和dom中常用的事件
//通過id來獲取元素 使用document.getElementsById("id名") 來獲取元素 //返回值為一個元素的物件 <body> <input type="button" value="修改列表背景顏色" id="btn"> &l
最大流問題的幾種模板和思路
核心思路: (首先感謝網路大牛程式碼 附大牛網址:https://blog.csdn.net/huzhengnan/article/details/7766446) 我們首先 採用搜尋的方式 來找到一條從 s-t 的路徑 並且儲存記錄剩
Cesium中的幾種座標和相互轉換
轉載:https://blog.csdn.net/qq_34149805/article/details/78393540 幾個重要的座標物件: 1.世界座標 Cartesian3:笛卡爾空間直角座標系 1 new Cesium.
05 識別毒酒——幾種演算法和編碼方式的分析和比較
說明 問題 識別毒酒 方法1 視為一個有約束的最優化問題進行求解 1 模型的進一步討論 3方法2 使用編碼的方法 1 結論 2 具體方法 3一個瑕疵和改進的方法
C/C++——strcpy函式的 幾種 實現 和 詳細 解析
C/C++——strcpy函式的實現 和解析 題目: 已知strcpy函式的原型是: &nb
字串中判斷存在的幾種模式和效率(string.contains、string.IndexOf、Regex.Match)
通常情況下,我們判斷一個字串中是否存在某值常常會用string.contains,其實判斷一個字串中存在某值的方法有很多種,最常用的就是前述所說的string.contains,相對來說比較常用的還有string.IndexOf和Regex.Match。直接上程式碼,後面在說些什麼吧,通常情況下功能的實現最
HTML中呼叫JavaScript的幾種情況和規範寫法
JavaScript執行在html中,引用有幾種方式? 我知道的方法有3種: 第一種:外部引用遠端JavaScript檔案。如<script type="text/javascript" src="../js/jquery-1.8.3.js"></script>(相對
map遍歷的幾種方式和效率問題
一、map遍歷的效率 先建立一個map,新增好資料: Map<String, String> map = new HashMap<>(); for (int i = 0; i < 1000000; i++) { map.put(i +
關於listview的自定義adapter不執行getview()方法的幾種情況和解決辦法
自定義PersonAdapter 繼承自BaseAdapter; PersonAdapter中getView不執行。 佈局使用lsitView 分析: 遇到此種情況 一、檢視adapter繫結的資料 d
oracle ORA 00936 missing expression 報錯的幾種情況和解決方法
ORA-00936: missing expression Cause: A required part of a clause or expression has been omitted. For example, a SELECT statement may have been entered wi
Vuex有那幾種狀態和屬性?
vuex的流程 頁面通過mapAction非同步提交事件到action。action通過commit把對應引數同步提交到mutation。mutation會修改state中對於的值。 最後通過getter把對應值跑出去,在頁面的計算屬性中,通過mapGett
Web開發中 前端路由 實現的幾種方式和適用場景
故事從名叫Oliver的綠箭蝦`說起,這位大蝦酷愛社交網站,一天他打開了 Twitter ,從發過的tweets的選項卡一路切到followers選項卡,Oliver發現頁面的內容變化了,URL也變化了,但為什麼頁面沒有閃爍重新整理呢?於是Oliver開啟的網路監控器(沒錯,Oliver是個程式設計師),他驚