**about業務主鍵和邏輯主鍵
1業務主鍵(natrual key),有意義的字段。 對前端可見
2邏輯主鍵(surrogate key),無意義的字段,即自增長字段,即identity。這其中還有一個選擇GUID。
如果有業務主鍵,數據唯一性就能得到保證,邏輯主鍵存在的意義?
盡量不要用GUID作 PRIMARY KEY.
PRIMARY KEY可以用多列,不一定非要使用以列
以下是一些反對用邏輯主鍵的理由,
--
1.They 're not standard SQL. Most products have it but there 's no consistent implementation.
2.They can 't be updated. This violates the relational data model (not fatal, but not good either). Duplicates can be accidentally inserted (fatal).
3.They only create numeric values. GUID/NewID() are also numeric only, and are hard to read.
4.Numeric values are not meaningful in many tables, and adding them complicates relationships between other tables.
--
Problems such as supporting replication, merging, and a providing a limited number of unique values for a heavy transactional systems
--
IDENTITY字段不要作為表的主鍵與其它表關聯,這將會影響到該表的數據遷移。
--
*viewPoint01
既然有業務主鍵,數據唯一性就能得到保證了。
那么你的邏輯主鍵的存在的意義或者是目的是什么呢?
邏輯主鍵的存在的意義在於當業務主鍵是多個字段組合的和/或業務主鍵的數據類型不便於比較的時候,用業務主鍵作為聚集索引影響性能,這個時候加一個冗余的數字型的邏輯主鍵顯得很必要,也會方便編程。
加邏輯主鍵應該主要是從性能考慮
*viewPoint02
我有不同的觀點:
我認為業務主鍵作為聚集索引反而更有利,因為在大數量的業務操作中,操作員能記住並交流的的就只有那么幾個有限的數據:業務單據號、物品編號(名稱)、客戶編號(名稱)。他們在查詢的時候最多就是這些,最多還要加上日期時間。數據庫查詢的壓力都來自他們。所以應該說好處是比較大的。
說到這里,我還要說個題外話。除開WEB的設計方式來講,企業的關鍵數據是不允許出現很多人在這里問的要怎么刪除重復數據的情況的。不是完全不能,只是回給大家帶來很多困擾,影響后面的流程。
話說回來,一個邏輯主鍵與一業務主鍵共存的數據集用哪個來與外界聯系?都可以!
那么邏輯主鍵不是流水號,每個表都有1和10,困擾也很大。
*viewPoint03
其實我覺得比較好的是兩種方法都用。
最好是業務主鍵做為聚集索引,identity做唯一索引。
identity並不適合做為聚集索引,因為 identity若做為聚集索引,性能不好(雖然我自己也沒感覺到)。用identity做為聚集索引,會造成局部熱點,也是就某個存儲區域過熱,這樣會造成大量的操作都在相鄰的存儲區域進行,會對性能造成影響。理論上我覺得是很有道理的,不過實際上我沒有什么體會。
業務主鍵做為聚集索引,會對查詢性能有很多好處。
*viewPoint04
“既然有業務主鍵,數據唯一性就能得到保證了”
引入 Identity,GUID 的原因就是業務主鍵的唯一性很難保證,因為自然主鍵來自實際業務,往往是人工編制而成,其唯一性不容易得到保證。
Identity 作主鍵,除了唯一性可以保證以外,還可以提高索引的速度,因為對索引而言, INT 數據類型的 Identity 的速度是最快的。
但我也很喜歡用identity(或無意義的關鍵字),因為在做程序時,用一個integer做為參數就可以了,不必用幾個參數,而且也方便移值給其它表或其它系統使用。用identity也有它的好處。
至於復制,如果要考慮分發復制的話,我覺得不必怪罪到identity頭上,你大可以用業務主鍵來做復制,為什么要用identity?
*viewPoint05
其實我在WEB開發的時候就是會用Identity做主鍵的,感覺比較爽。
但是換過來我在做企業管理軟件的時候就是采用‘自定義’業務主鍵了。所謂自定義就是在一開始由用戶定義一個格式比如,長度,打頭字母,起始編號等,當然這個編號最后還是依據用戶定義自增長的。所以從來都沒有出現過主鍵重復過:)
而且用戶也樂意使用這樣的業務主鍵。
*viewPoint06
對老外的話(他是邏輯主鍵的反對方),我覺的他說的可能是這種情況(當然各位可能已經理解了):
當手工合並兩張表時,這兩張表的IDENTITY字段很可能都是1,2,3,4,5........,這樣問題出現了,主鍵不唯一,首先你需要重建IDENTITY來保證主鍵唯一,另外引用他們的外鍵全得修改。
壇子里我想有很多人都認為該用邏輯主鍵,但象上面這種情況各位是否考慮過?處理過?
更棘手的問題:
幾個分公司的全部數據(其中有很多父子表),定時發送到總公司。分公司、總公司表結構一樣,只要用到IDENTITY,真的很麻煩了...
*viewPoint07
我以前經常處理這樣的表,只要頭腦清楚就行了。麻煩是麻煩了一點,但誰會天天去倒數據呢?
總公司和分公司的情況,你可以另加一個字段branchID,用branchID + ID做為唯一鍵。
用identity的一些好處也是有的,比如做三層結構的設計時,不同的單據共用相同的對象模型或公用函數,但不同的單據的業務關鍵字都不一樣,就會很不好做統一的接口,用identity則所有的單據都只用ID做為參數就可以了,問題迎刃而解。
所以用不用,可以按實際情況決定取舍。
A:icevi(按鈕工廠) 前面不是說了一個方法了嗎,就是加上一個表示分支的branchID。用branchID+IDENTITY組合成合並表的主鍵。
*viewPoint08
用identity確實有些問題,首先是合並復制問題。
MS給出3種方案:
1Automatic Identity Range Handling。
2Manual Identity Range Handling。即NOT FOR REPLICATION 。
3Using Other Columns as Primary Keys。這第3種方案即icevi(按鈕工廠)所說的方法。
我現在的問題:這3種方法中,各位認為第3種方案是最好的嗎?只是覺得有些麻煩,每個有INDENTITY的表都得有個BranchID;如果我每個表都有IDENTITY,編碼時,每一次增,刪,改,查都要跟上BranchID,噩夢?
因為公司業務經常是無法預見的,開發時還是一個公司,兩年后,可能全國各地好多子公司,按着這種思路,開發之初就應該加BranchID。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
定義(部分定義來源於 SQL Server 聯機叢書):
主鍵(PRIMARY KEY):表通常具有包含唯一標識表中每一行的值的一列或一組列。這樣的一列或多列稱為表的主鍵 (PK),用於強制表的實體完整性。
外鍵(FOREIGN KEY):外鍵 (FK) 是用於建立和加強兩個表數據之間的鏈接的一列或多列。在外鍵引用中,當一個表的列被引用作為另一個表的主鍵值的列時,就在兩表之間創建了鏈接。這個列就成為第二個表的外鍵。
聚集索引:聚集索引基於數據行的鍵值在表內排序和存儲這些數據行。每個表只能有一個聚集索引,因為數據行本身只能按一個順序存儲。
非聚集索引:非聚集索引包含索引鍵值和指向表數據存儲位置的行定位器。可以對表或索引視圖創建多個非聚集索引。通常,設計非聚集索引是為改善經常使用的、沒有建立聚集索引的查詢的性能。
自動編號列和標識符列:對於每個表,均可創建一個包含系統生成的序號值的標識符列,該序號值以唯一方式標識表中的每一行。
業務主鍵(自然主鍵):在數據庫表中把具有業務邏輯含義的字段作為主鍵,稱為“自然主鍵(Natural Key)”。
邏輯主鍵(代理主鍵):在數據庫表中采用一個與當前表中邏輯信息無關的字段作為其主鍵,稱為“代理主鍵”。
復合主鍵(聯合主鍵):通過兩個或者多個字段的組合作為主鍵。
原理分析:
使用邏輯主鍵的主要原因是,業務主鍵一旦改變則系統中關聯該主鍵的部分的修改將會是不可避免的,並且引用越多改動越大。而使用邏輯主鍵則只需要修改相應的業務主鍵相關的業務邏輯即可,減少了因為業務主鍵相關改變對系統的影響范圍。業務邏輯的改變是不可避免的,因為“永遠不變的是變化”,沒有任何一個公司是一成不變的,沒有任何一個業務是永遠不變的。最典型的例子就是身份證升位和駕駛執照號換用身份證號的業務變更。而且現實中也確實出現了身份證號碼重復的情況,這樣如果用身份證號碼作為主鍵也帶來了難以處理的情況。當然應對改變,可以有很多解決方案,方案之一是做一新系統與時俱進,這對軟件公司來說確實是件好事。
使用邏輯主鍵的另外一個原因是,業務主鍵過大,不利於傳輸、處理和存儲。我認為一般如果業務主鍵超過8字節就應該考慮使用邏輯主鍵了,因為int是4字節的,bigint是8字節的,而業務主鍵一般是字符串,同樣是 8 字節的 bigint 和 8 字節的字符串在傳輸和處理上自然是 bigint 效率更高一些。想象一下 code == "12345678" 和 id == 12345678 的匯編碼的不同就知道了。當然邏輯主鍵不一定是 int 或者 bigint ,而業務主鍵也不一定是字符串也可以是 int 或 datetime 等類型,同時傳輸的也不一定就是主鍵,這個就要具體分析了,但是原理類似,這里只是討論通常情況。同時如果其他表需要引用該主鍵的話,也需要存儲該主鍵,那么這個存儲空間的開銷也是不一樣的。而且這些表的這個引用字段通常就是外鍵,或者通常也會建索引方便查找,這樣也會造成存儲空間的開銷的不同,這也是需要具體分析的。
使用邏輯主鍵的再一個原因是,使用 int 或者 bigint 作為外鍵進行聯接查詢,性能會比以字符串作為外鍵進行聯接查詢快。原理和上面的類似,這里不再重復。
使用邏輯主鍵的再一個原因是,存在用戶或維護人員誤錄入數據到業務主鍵中的問題。例如錯把 RMB 錄入為 RXB ,相關的引用都是引用了錯誤的數據,一旦需要修改則非常麻煩。如果使用邏輯主鍵則問題很好解決,如果使用業務主鍵則會影響到其他表的外鍵數據,當然也可以通過級聯更新方式解決,但是不是所有都能級聯得了的。
使用業務主鍵的主要原因是,增加邏輯主鍵就是增加了一個業務無關的字段,而用戶通常都是對於業務相關的字段進行查找(比如員工的工號,書本的 ISBN No. ),這樣我們除了為邏輯主鍵加索引,還必須為這些業務字段加索引,這樣數據庫的性能就會下降,而且也增加了存儲空間的開銷。所以對於業務上確實不常改變的基礎數據而言,使用業務主鍵不失是一個比較好的選擇。另一方面,對於基礎數據而言,一般的增、刪、改都比較少,所以這部分的開銷也不會太多,而如果這時候對於業務邏輯的改變有擔憂的話,也是可以考慮使用邏輯主鍵的,這就需要具體問題具體分析了。
使用業務主鍵的另外一個原因是,對於用戶操作而言,都是通過業務字段進行的,所以在這些情況下,如果使用邏輯主鍵的話,必須要多做一次映射轉換的動作。我認為這種擔心是多余的,直接使用業務主鍵查詢就能得到結果,根本不用管邏輯主鍵,除非業務主鍵本身就不唯一。另外,如果在設計的時候就考慮使用邏輯主鍵的話,編碼的時候也是會以主鍵為主進行處理的,在系統內部傳輸、處理和存儲都是相同的主鍵,不存在轉換問題。除非現有系統是使用業務主鍵,要把現有系統改成使用邏輯主鍵,這種情況才會存在轉換問題。暫時沒有想到還有什么場景是存在這樣的轉換的。
使用業務主鍵的再一個原因是,對於銀行系統而言安全性比性能更加重要,這時候就會考慮使用業務主鍵,既可以作為主鍵也可以作為冗余數據,避免因為使用邏輯主鍵帶來的關聯丟失問題。如果由於某種原因導致主表和子表關聯關系丟失的話,銀行可是會面臨無法挽回的損失的。為了杜絕這種情況的發生,業務主鍵需要在重要的表中有冗余存在,這種情況最好的處理方式就是直接使用業務主鍵了。例如身份證號、存折號、卡號等。所以通常銀行系統都要求使用業務主鍵,這個需求並不是出於性能的考慮而是出於安全性的考慮。
使用復合主鍵的主要原因和使用業務主鍵是相關的,通常業務主鍵只使用一個字段不能解決問題,那就只能使用多個字段了。例如使用姓名字段不夠用了,再加個生日字段。這種使用復合主鍵方式效率非常低,主要原因和上面對於較大的業務主鍵的情況類似。另外如果其他表要與該表關聯則需要引用復合主鍵的所有字段,這就不單純是性能問題了,還有存儲空間的問題了,當然你也可以認為這是合理的數據冗余,方便查詢,但是感覺有點得不償失。
使用復合主鍵的另外一個原因是,對於關系表來說必須關聯兩個實體表的主鍵,才能表示它們之間的關系,那么可以把這兩個主鍵聯合組成復合主鍵即可。如果兩個實體存在多個關系,可以再加一個順序字段聯合組成復合主鍵,但是這樣就會引入業務主鍵的弊端。當然也可以另外對這個關系表添加一個邏輯主鍵,避免了業務主鍵的弊端,同時也方便其他表對它的引用。
綜合來說,網上大多數人是傾向於用邏輯主鍵的,而對於實體表用復合主鍵方式的應該沒有多少人認同。支持業務主鍵的人通常有種誤解,認為邏輯主鍵必須對用戶來說有意義,其實邏輯主鍵只是系統內部使用的,對用戶來說是無需知道的。
結論或推論:
1、盡量避免使用業務主鍵,盡量使用邏輯主鍵。
2、如果要使用業務主鍵必須保證業務主鍵相關的業務邏輯改變的概率為0,並且業務主鍵不太大,並且業務主鍵不能交由用戶修改。
3、除關系表外,盡量不使用復合主鍵。
使用邏輯主鍵的最佳實踐指南:
1、足夠用就好。系統使用的生命周期以100年為限,邏輯主鍵數據類型采用下表規則,如果不確定則使用int類型。
數據量 | 數據類型 | 數據大小 | 生成頻率 | 備注 |
< 128 | tinyint | 1 字節 | 1條/年 | 頻率過低,不太靠譜,不建議采用 |
< 3 萬 | smallint | 2 字節 | 27條/月 | 頻率較低,慎用 |
< 21 億 | int | 4 字節 | 40條/分鍾 | 能滿足大部分情況 |
< 922 億億 | bigint | 8 字節 | 292萬條/毫秒 | 能滿足絕大部分情況 |
>= 922 億億 | uniqueidentifier | 16 字節 | 100億用戶同時每毫秒生成10億條,可以連續生成10億年 |
可用於分布式、高並發的應用 |
2、一般采用自增長方式或NewID()方式。
3、主鍵字段名稱一般采用“表名ID”方式,方便識別和表聯接。
4、如果表存在分布式應用,則可以考慮采用不同起始值,相同步長方式自增。例如有3個部署在不同地方的庫,則可以如下設計:
起始值 | 步長 |
1 | 10 |
2 | 10 |
3 | 10 |
步長統一設置10是為了方便日后擴展,這樣不同庫之間也能保持主鍵唯一性了,也方便合並。
5、如果存在高並發性需求或數據表遷移需求,可以考慮使用uniqueidentifier類型,並使用NewID()函數。
6、可以考慮對業務主鍵建立唯一性索引,以實現業務主鍵唯一性的業務需求。
7、如果需要考慮業務主鍵的性能需求,可以把業務主鍵建立聚集索引,而邏輯主鍵只建立主鍵約束和非聚集索引即可。
8、關系表可以考慮采用復合主鍵方式,復合主鍵不用於實體表。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*viewPoint09
1、從純數據庫設計的角度來看:
使用業務主鍵是自然的選擇,但如果業務發生變化引起業務主鍵的變化怎么辦?(xiaoyu舉的例子),所以引入邏輯主鍵id(當然不是每張表都要),浪費空間之說我認為是沒有必要考慮的,存儲技術的發展能容忍這點小浪費。而且單一的id在多表關聯時,顯然比復合主鍵有更好的性能。
2、從WEB應用開發的角度來看:
用邏輯主鍵你只需要在頁面和后台之間傳遞一個單一的參數,簡化了開發工作,而且有較高的查詢效率。
3、從Hibernate應用的角度來說:
復合主鍵需要更多的處理,性能不高,所以Hibernate不提倡用復合主鍵