1. 程式人生 > >理解資料庫的幾種鍵和幾個正規化

理解資料庫的幾種鍵和幾個正規化

  在上學的時候,資料庫是一門讓我比較頭大的課程。記得當時教材上淨是一些晦澀難懂的語言,沒有充足的例項來幫助理解。前一陣子在看《網路遊戲伺服器端程式設計》的過程中,突然對資料庫正規化有了一些感覺,在此總結一下,分享給大家。作者純菜鳥,即使總結這些基礎知識也難免有錯,希望給位大牛不吝賜教,謝謝!

  鍵(關係鍵)以及資料庫正規化都是關係資料庫的概念。所謂關係鍵,指的是一個表中的一個(或一組)屬性,用來標識該表的每一行與另一個表產生聯絡

  資料庫的”正規化“,指的是設計資料庫的規則。按照一定的規則設計出資料庫的表和關係,能夠避免在一些情況下的查詢出錯,並具有良好的結構。總的來說,隨著正規化等級的提高,資料表屬性之間的依賴關係越來越小,資料冗餘越來越低。但同時,資料關係變得更加複雜,訪問一個具體資料的關係層次增加。

所以像設計模式一樣,不應盲目追求正規化等級,應根據具體需求來選擇正規化。

  我們先來看一下幾種常見的資料庫關係鍵:

  1、超鍵(super key):能夠唯一標識一條記錄的屬性或屬性集。

    • 標識性:一個數據表的所有記錄都具有不同的超鍵
    • 非空性:不能為空

  2、候選鍵(candidate key):能夠唯一標識一條記錄的最小屬性集

    • 標識性:一個數據表的所有記錄都具有不同的候選鍵
    • 最小性:候選鍵的任何子集都不能唯一標識一個記錄
    • 非空性:不能為空
    • 候選鍵是沒有多餘屬性的超鍵

  3、主鍵(主碼、primary key)某個能夠唯一標識一條記錄的最小屬性集

    • 唯一性:一個數據表只能有一個主鍵
    • 標識性:一個數據表的所有記錄都具有不同的主鍵取值
    • 非空性:不能為空
    • 選取某個候選鍵為主鍵

  4、外來鍵(foreign key):子資料表中出現的父資料表的主鍵,稱為子資料表的外來鍵。

  5、代理鍵:當不適合用任何一個候選鍵作為主鍵時(如資料太長等),新增一個沒有實際意義的鍵作為主鍵,這個鍵就是代理鍵。(如常用的序號1、2、3)

  6、自然鍵:自然生活中唯一能夠標識一條記錄的鍵(如身份證)

  下面就來看一下常見的幾種關係資料庫正規化吧。

一、第一正規化(1NF)

  要求:

  •   每一個屬性都不能再分割,都是原子項。

  第一正規化是關係型資料表的基本要求,但是如何判斷一個屬效能否再分割呢?這沒有統一的標準,需要依照需求確定

。比如,我們要設計一個網路遊戲後臺所用的資料庫,其中有一個數據表,記錄有關於擊殺怪物所獲得的金錢:

編號

怪物名

掉落金錢

1

巨熊

100

   這個表格看上去並沒有什麼問題。每一個屬性項都是”不可分割“的,所以符合第一正規化。但是,如果我們希望把玩家擊殺怪物之後獲得的金錢分成兩部分,一部分是固定收益,另一部分是一個隨機的浮動收益(比如和玩家幸運值有關)。則這張表格中的”掉落金錢“項就不是”不可分割“了,也就不符合第一正規化了。如果有這種需求,我們就可以把”掉落金錢“分割為”固定金錢“和”浮動金錢“兩部分。如下所示:

編號

怪物名

固定金錢

浮動金錢

1

巨熊

80

20

  這樣分割之後,使得每一項都不能再分割,從而使得資料表滿足第一正規化。 

  滿足第一正規化的資料表有什麼好處呢?

  1. 1NF保證了資料庫的每一列都是不同的。每一列的資料彼此沒有任何交集。
  2. 這樣做首先減少了資料的冗餘,節省儲存空間。如果不滿足第一正規化,一些資料項有可能包含相同的”子項“,造成儲存空間的浪費。
  3. 其次,每一列沒有重複的資料意味著不需要考慮資料更新的同步問題。不用擔心在一列中更新了資料,還要在另一列做相應修改。
  4. 另外,每一列的資料不可再分,在某些情況下少了資料訪問的層數,提高資料訪問速度。

二、第二正規化(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

  表中的依賴關係有:

  1. (玩家ID,房間ID)-> 房主ID
  2. 房主ID -> 房間ID
  3. (玩家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

  單獨看這三個子表,我們可以得出以下結論:

  1. Alice和Bob對戰過
  2. Alice在競技場1和競技場2都進行過對戰
  3. 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是個程式設計師),他驚