选择主键:自然键还是代理键?
【轉自】http://blog.csdn.net/haiross/article/details/21388997
【原文地址】http://www.agiledata.org/essays/keys.html
本文概述關系數據庫中為表指定主鍵的策略。主要關注于何時使用自然鍵或者代理鍵的問題。有些人會告訴你應該總是使用自然鍵,而另外一些人會告訴你應該總是使用代理鍵。這些人總是被證明是錯誤的,通常他們僅僅是與你分享了他們“數據信仰”的偏見。事實上自然鍵與代理鍵具有各自的優缺點,沒有在所有情況下都完美的策略。也就是說,你必須清楚你要做的事情才能做好它。本文討論以下內容:
常用的術語
選擇鍵的策略
代理鍵實現策略
有效選鍵的技巧
做出了“錯誤”選擇時如何應對
1 常用的術語
讓我們從描述一些關于鍵的常用術語開始,然后再看一個示例。這些術語包括:
1.鍵。鍵是唯一標識一個實體的一個或者多個數據屬性。在物理數據庫中,鍵可以由表的一個或者多個列組成,它們的值唯一標識關系表中的一行。
2.復合鍵。由兩個或者多個屬性組成的鍵。
3.自然鍵。由現實世界中已經存在的屬性組成的鍵。例如,美國公民被分配了一個唯一(不保證一定正確,但實際上非常接近唯一)的社會保險號(SSN)。如果隱私法允許的話,SSN可能被用作Person實體的自然鍵(假設組織的人員范圍僅限于美國)。
4.代理鍵。不具有業務含義的鍵。
5.候選鍵。在邏輯數據模型中的實體類型可能具有0或多個候選鍵,簡稱為唯一標識(注解:某些人不主張在邏輯數據模型中標識候選鍵,因此沒有固定標準)。例如,假設我們只涉及美國公民,那么SSN是Person實體類型的一個候選鍵,同時姓名與電話號碼的組合(假設組合是唯一的)是第二個可能的候選鍵。這兩個鍵都被稱作候選鍵是因為它們是物理數據模型中主鍵、次鍵或者非鍵的候選目標。
6.主鍵。實體類型的首選鍵。
7.備用鍵。也就是次鍵,是表中行的另一個唯一標識。
8.外鍵。在一個實體類型中表示另一個實體類型的主鍵或者次鍵的一個或多個屬性。
圖1顯示了使用UML符號描述的實際地址的物理數據模型。在圖1中,表Customer使用CustomerNumber作為主鍵而SocialSecurityNumber作為備用鍵。這就意味著訪問顧客信息的首先方法是通過一個人的顧客編號,雖然軟件使用社會保險號也能夠獲得相同的信息。表CustomerHasAddress擁有一個復合鍵,由CustomerNumber與AddressID組成。外鍵是一個實體類型中,代表另一個實體類型的主鍵或者次鍵的一個或多個屬性。外鍵用于維護數據行之間的關聯。例如,表CustomerHasAddress與表Customer中行之間的關聯通過表CustomerHasAddress的CustomerNumber列進行維護。有趣的是列CustomerNumber既是表CustomerHasAddress主鍵的一部分,又是表Customer的外鍵。同樣,列AddressID既是表CustomerHasAddress主鍵的一部分,又是表Address的外鍵,維護這兩個表中行的關聯。
圖1 一個包含Customer與Address的簡單PDM
2 比較自然鍵與代理鍵策略
為表指定鍵的策略有兩種:
1.自然鍵。自然鍵是已經存在的一個或多個屬性,它在業務概念中是唯一的。對于表Customer來說,存在兩個候選鍵,CustomerNumber與SocialSecurityNumber。
2.代理鍵。引入一個不具有業務含義的列作為鍵,稱作代理鍵。例如圖1中表Address的列AddressID。地址不具有一個“簡單”的自然鍵,因為需要使用Address表的所有列組成一個鍵(取決于你的問題域,可能僅僅需要組合Street和ZipCode列),所以此時引入一個代理鍵是一個更好的選擇。
自然鍵的優點是它們已經存在,不需要在數據模式中引入一個新的“非自然”列。然而,自然鍵的缺點是由于具有業務含義,它們與業務直接耦合:你可能在業務需求變更時重新指定鍵。例如,當你的用戶決定將CustomerNumber列從數字型改為字母數字型,除了更新表Customer的模式(這個是不可避免的)外,你還需要修改每一個使用CustomerNumber作為外鍵的表。
代理鍵具有幾個優點。首先,它們不與業務耦合,因此更容易維護(假設你選擇了一個好的實現策略)。例如,如果表Customer改為使用代理鍵,修改只需要在表Customer內部進行(此時CustomerNumber只是表的一個非鍵列)。當然,如果你需要針對代理鍵策略做相似的變更,可能是由于用完了所有的值而需要增加幾個位數,將會面臨同樣的問題。其次,一個大多數表,最好是全部表,通用的鍵策略能夠減少需要編寫的源碼數量,減少系統的總體擁有成本(TCO)。代理鍵根本的缺點是它們通常不是“人可讀的”,導致終端用戶難以使用。這意味著你可能仍然需要實現代理鍵用于查找、編輯等等。
根本問題在于鍵是關系模式中重要的耦合源,因此它們很難更改。這意味著你通常想要避免具有業務含義的鍵,因為業務含義存在變化。話雖如此,我傾向于使用自然鍵查找/引用表,尤其當我認為鍵值在最近不會改變時,如下文所述。從根本上講,是否應該優先使用自然鍵沒有明確的答案,不管這個宗教之爭的另外一方狂熱者如何聲稱,最好的策略是只要有意義就可以使用任何一個策略。
3 代理鍵實現策略
實現代理鍵有幾個常用的選擇:
1.使用數據庫賦值。大多數主要的數據庫供應商–例如Oracle、Sybase以及Informix–實現了被稱為遞增鍵的代理鍵策略?;纠砟钍窃跀祿旆掌髦芯S護一個計數器,將當前值寫入一個隱藏的系統表來維護一致性,并用于賦值一個新建的數據行。每創建一行,計數器遞增并將值作為該行的鍵值。不同供應商的實現策略不同,有時候值在所有表之間都是唯一的,有時候只在單個表內部是唯一的,但是基本概念相同。
2.MAX() + 1。一個常用的策略是使用整數列,第一條記錄從1開始,然后新行的值設置為該列的最大值加1,最大值用SQL函數MAX獲得。雖然這個方法簡單,但是對于大表存在性能問題,而且它只能確保表內部的唯一鍵值。
3.全局唯一標識符(UUIDs)。GUIDs是128位值,來自以太網卡ID或等價的軟件表示以及系統當前時間的哈希值。該算法是由開放軟件基金會定義的。
4.全球唯一標識(GUIDs)。GUIDs是微軟擴展UUIDs后的標準,遵從相同的策略,如果存在以太網卡使用網卡ID,如果不存在,使用軟件ID與當前時間計算一個哈希值,確保在機器內部唯一。
5.高低位策略。它的基本思想是鍵值,通常稱為持久化對象標識符(POID)或者簡稱對象標識符(OID),分為兩個邏輯部分:從指定來源獲取的唯一HIGH值和應用自身分配的N為LOW值。每獲取一個HIGH值,LOW值設置為0。例如,應用請求一個HIGH值并被賦予1701。假設LOW值的位數N為4,那么賦予對象的POID將會由17010000、17010001、17010002等等直到17019999組成。此時,再獲取一個新的HIGH值,LOW值設置為0,再次重復。如果另一個應用在之后立即請求了一個HIGH值,它將獲得1702,而它創建的對象被賦予OIDs將會是17020000、17020001等等。正如你所看到的,只要HIGH值唯一,所有的POID值將會唯一。在www.theserverside.com上可以找到一個HIGH-LOW發生器的實現。
根本問題在于鍵是關系模式中重要的耦合源,因此它們很難重構。這意味著你通常想要避免具有業務含義的鍵,因為業務含義存在變化。然而,同時你需要記住某些數據通常是通過唯一標識進行訪問,例如通過顧客編號訪問顧客信息,通過社會保險號訪問美國雇員信息。在這種情況下你可能想要使用自然鍵而不是UUID或者POID這樣的代理鍵。
4 有效選鍵技巧
如何有效地選擇鍵?參考以下提示:
1.避免“智能”鍵?!爸悄堋辨I是由一個或多個具有業務含義的部分組成的鍵。例如,美國郵政編碼的前兩位表示它所在的州。智能鍵的第一個問題是它具有業務含義。其次是它們的使用通常隨著時間變得很復雜。例如,一些大的州擁有多個代碼,加利福尼亞的郵編以90和91開頭,導致基于州編碼的查詢更加復雜。第三個問題是它們通常增加了策略需要進行擴展的可能性??紤]長度為9位數字的郵編(后4位數字由建筑所有者自行決定,建筑由郵編唯一標識),在用完2位州代碼前用完9位數字的可能性更小。
2.考慮為簡單的“查找”表指定自然鍵?!安檎摇北硎怯糜陉P聯代碼與詳細信息的表。例如,你可能擁有一個列出了顏色代碼對應顏色名稱的查找表。例如,代碼127代表“郁金香黃色”。簡單的查找表通常包含一個代碼列和一個描述/名稱列,而復雜的查找表包含一個代碼列和幾個信息列。
3.自然鍵并非總是適用于“查找”表。另一個例子是一個查找表包含北美洲的州、省或者地區。例如美國的加利福尼亞州以及加拿大的安大略省。該表的主要目的是為這些地理位置提供一個正式的列表,它不會隨時間變化(最近一次變化是90年代后期,加拿大的西北地區分割為努勒維特和西北地區)。該表的一個有效自然鍵可以是州代碼,一個值唯一的兩字符代碼–例如加利福尼亞的CA以及安大略的ON。不幸的是這種方法并不適合,因為加拿大政府決定為西北地區兩個州使用相同的代碼NW。
4.應用必須仍然支持“自然鍵搜索”。如果選擇采用代理鍵,必須不能忘了應用需要支持基于地域列(仍然唯一標識數據行)的搜索。例如,Customer表可能擁有一個Customer_POID的代理鍵,以及一個Customer_Number列和Social_Security_Number列。你很可能需要支持基于顧客編號和社會保險號的搜索。搜索在關系數據庫對象檢索最佳實踐中詳細討論。
5.不要自然化代理鍵。一旦你向終端用戶顯示了代理鍵的值,或者更壞的是允許他們使用該值(例如搜索該值),實際上你已經給它們賦予了業務含義。這實際上是自然化了代理鍵從而失去了代理鍵的優點。
5 做出了“錯誤”選擇時如何應對
首先,不用為此擔心:不論你多么擅長數據庫設計都可能會犯錯。好消息是正如我在數據庫重構過程中所說,雖然可能需要許多工作,還是可以使用代理鍵替換自然鍵的(反之亦然)。要使用代理鍵替換自然鍵,你需要應用引入代理鍵重構,如圖2所示。要使用自然鍵替換代理鍵,你需要應用使用自然鍵替換代理鍵重構,如圖3所示。
圖2 替換表Order的自然鍵
圖3 替換表State的代理鍵
總結
以上是生活随笔為你收集整理的选择主键:自然键还是代理键?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PostgreSQL中Timestamp
- 下一篇: 在民主性原则中如何贯彻落实全过程人民民主