小議sqlserver數據庫主鍵拔取戰略。本站提示廣大學習愛好者:(小議sqlserver數據庫主鍵拔取戰略)文章只能為提供參考,不一定能成為您想要的結果。以下是小議sqlserver數據庫主鍵拔取戰略正文
由於主鍵可以獨一標識某一行記載,所以可以確保履行數據更新、刪除的時刻不會湧現張冠李戴的毛病。固然,其它字段可以幫助我們在履行這些操作時清除同享抵觸,不外就不在這裡評論辯論了。主鍵除上述感化外,經常與外鍵組成參照完全性束縛,避免湧現數據紛歧致。所以數據庫在設計時,主鍵起到了很主要的感化。
罕見的數據庫主鍵拔取方法有:
主動增加字段
手動增加字段
UniqueIdentifier
“COMB(Combine)”類型
1、主動增加型字段
許多數據庫設計者愛好應用主動增加型字段,由於它應用簡略。主動增加型字段許可我們在向數據庫添加數據時,不斟酌主鍵的取值,記載拔出後,數據庫體系會主動為其分派一個值,確保相對不會湧現反復。假如應用SQL Server數據庫的話,我們還可以在記載拔出後應用@@IDENTITY全局變量獲得體系分派的主鍵鍵值。
雖然主動增加型字段會免卻我們許多繁瑣的任務,但應用它也存在潛伏的成績,那就是在數據緩沖形式下,很難事後填寫主鍵與外鍵的值。假定有兩張表:
Order(OrderID, OrderDate)
OrderDetial(OrderID, LineNum, ProductID, Price)
Order表中的OrderID是主動增加型的字段。如今須要我們錄入一張定單,包含在Order表中拔出一筆記錄和在OrderDetail表中拔出若干筆記錄。由於Order表中的OrderID是主動增加型的字段,那末我們在記載正式拔出到數據庫之前沒法事前得知它的取值,只要在更新後能力曉得數據庫為它分派的是甚麼值。這會形成以下抵觸產生:
起首,為了能在OrderDetail的OrderID字段中添入准確的值,必需先更新Order表以獲得到體系為其分派的OrderID值,然後再用這個OrderID填充OrderDetail表。最初更新OderDetail表。然則,為了確保數據的分歧性,Order與 OrderDetail在更新時必需在事務掩護下同時停止,即確保兩表同時更行勝利。明顯它們是互相抵觸的。(此處表述有毛病。呂震宇 2005-6-15)
【彌補2005-6-15】---------------------------------------------
聽棠.NET指出:主檔放在事務中提交時,經由過程@@IDENTITY 便可以取到生成值的,是以可以傳給明細當外鍵用,並且在事務產生毛病回滾時,主檔記載也會被回滾撤消的。
呂震宇彌補:應用主動增加字段會增長收集的roundTrip。雖然可使用@@IDENTITY獲得主鍵的值,但在更新進程中,不能不增長一次數據往復(以C/S構造為例):
1、客戶端發送開端事務敕令
2、客戶端提交主表更新
3、辦事器前往@@IDENTITY
4、客戶端依據前往的主鍵更新從表緩沖
5、客戶端將從表提交辦事器更新
6、客戶端提交事務
在這裡多了一次往復就會增長了事務處置的時光。下降並發機能。
假如不消主動增加型字段,將是以下情形:
1、客戶端發送開端事務敕令
2、客戶端提交主表更新
3、客戶端提交從表更新
4、客戶端提交事務
是以我不贊同應用主動增加型字段作為主鍵與外鍵鏈接的紐帶。
------------------------------------------------
除此以外,當我們須要在多個數據庫間停止數據的復制時(SQL Server的數據分發、定閱機制許可我們停止庫間的數據復制操作),主動增加型字段能夠形成數據歸並時的主鍵抵觸。假想一個數據庫中的Order表向另外一個庫中的Order表復制數據庫時,OrderID究竟該不應主動增加呢?
ADO.NET許可我們在DataSet中將某一個字段設置為主動增加型字段,但萬萬記住,這個主動增加字段僅僅是個占位符罷了,當數據庫停止更新時,數據庫生成的值會主動代替ADO.NET分派的值。所認為了避免用戶發生誤會,建議年夜家將ADO.NET中的主動增加初始值和增量都設置成-1。另外,在ADO.NET中,我們可認為兩張表樹立DataRelation,如許存在級聯關系的兩張表更新時,一張表更新後別的一張表對應鍵的值也會主動產生變更,這會年夜年夜削減了我們對存在級聯關系的兩表間更新時主動增加型字段帶來的費事。
2、手動增加型字段
既然主動增加型字段會帶來如斯的費事,我們無妨斟酌應用手動增加型的字段,也就是說主鍵的值須要本身保護,平日情形下須要樹立一張零丁的表存儲以後主鍵鍵值。還用下面的例子來講,此次我們新建一張表叫IntKey,包括兩個字段,KeyName和KeyValue。就像一個HashTable,給一個KeyName,便可以曉得今朝的KeyValue是甚麼,然背工工完成鍵值數據遞增。在SQL Server中可以編寫如許一個存儲進程,讓取鍵值的進程主動停止。代碼以下:
CREATE PROCEDURE [GetKey]
@KeyName char(10),
@KeyValue int OUTPUT
AS
UPDATE IntKey SET @KeyValue = KeyValue = KeyValue + 1 WHERE KeyName = @KeyName
GO
如許,經由過程挪用存儲進程,我們可以取得最新鍵值,確保不會湧現反復。若將OrderID字段設置為手動增加型字段,我們的法式可以由以下幾步來完成:起首挪用存儲進程,取得一個OrderID,然後應用這個OrderID填充Order表與OrderDetail表,最初在事務掩護下對兩表停止更新。
應用手動增加型字段作為主鍵在停止數據庫間數據復制時,可以確保數據歸並進程中不會湧現鍵值抵觸,只需我們為分歧的數據庫分派分歧的主鍵取值段就好了。然則,應用手動增加型字段會增長收集的RoundTrip,我們必需經由過程增長一次數據庫拜訪來獲得以後主鍵鍵值,這會增長收集和數據庫的負載,當處於一個低速或斷開的收集情況中時,這類做法會有很年夜的弊病。同時,手工保護主鍵還要斟酌並發抵觸等各種身分,這更會增長體系的龐雜水平。
3、應用UniqueIdentifier
SQL Server為我們供給了UniqueIdentifier數據類型,並供給了一個生成函數NEWID( ),應用NEWID( )可以生成一個獨一的UniqueIdentifier。UniqueIdentifier在數據庫中占用16個字節,湧現反復的幾率異常小,以致於可以以為是0。我們常常從注冊表中看到相似
{45F0EB02-0727-4F2E-AAB5-E8AEDEE0CEC5}
的器械現實上就是一個UniqueIdentifier,Windows用它來做COM組件和接口的標識,避免湧現反復。在.NET裡管 UniqueIdentifier稱之為GUID(Global Unique Identifier)。在C#中可使用以下敕令生成一個GUID:
Guid u = System.Guid.NewGuid();
關於下面提到的Order與OrderDetail的法式,假如選用UniqueIdentifier作為主鍵的話,我們完整可以免下面提到的增長收集RoundTrip的成績。經由過程法式直接生成GUID填充主鍵,不消斟酌能否會湧現反復。
UniqueIdentifier字段也存在嚴重的缺點:起首,它的長度是16字節,是整數的4倍長,會占用年夜量存儲空間。更加嚴重的是,UniqueIdentifier的生成毫無紀律可言,要想在下面樹立索引(絕年夜多半數據庫在主鍵上都有索引)是一個異常耗時的操作。有人做過試驗,拔出異樣的數據量,應用UniqueIdentifier型數據做主鍵要比應用Integer型數據慢,所以,出於效力斟酌,盡量防止應用 UniqueIdentifier型數據庫作為主鍵鍵值。
4、應用“COMB(Combine)”類型
既然下面三種主鍵類型拔取戰略都存在各自的缺陷,那末究竟有無好的方法加以處理呢?謎底是確定的。經由過程應用COMB類型(數據庫中沒有COMB類型,它是Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中設計出來的),可以在三者之間找到一個很好的均衡點。
COMB數據類型的根本設計思緒是如許的:既然UniqueIdentifier數據因毫無紀律可言形成索引效力低下,影響了體系的機能,那末我們能不克不及經由過程組合的方法,保存UniqueIdentifier的前10個字節,用後6個字節表現GUID生成的時光(DateTime),如許我們將時光信息與UniqueIdentifier組合起來,在保存UniqueIdentifier的獨一性的同時增長了有序性,以此來進步索引效力。或許有人會擔憂UniqueIdentifier削減到10字節會形成數據湧現反復,其實不消擔憂,後6字節的時光精度可以到達1/300秒,兩個COMB類型數據完整雷同的能夠性是在這1/300秒內生成的兩個GUID前10個字節完整雷同,這簡直是弗成能的!在SQL Server頂用SQL敕令將這一思緒完成出來就是:
DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(CAST(NEWID() AS BINARY(10))
+ CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)
經由測試,應用COMB做主鍵比應用INT做主鍵,在檢索、拔出、更新、刪除等操作上依然顯慢,但比Unidentifier類型要快上一些。關於測試數據可以參考我2004年7月21日的漫筆。
除應用存儲進程完成COMB數據外,我們也能夠應用C#生成COMB數據,如許一切主鍵生成任務可以在客戶端完成。C#代碼以下:
//================================================================
///<summary>
/// 前往 GUID 用於數據庫操作,特定的時光代碼可以進步檢索效力
/// </summary>
/// <returns>COMB (GUID 與時光混雜型) 類型 GUID 數據</returns>
public static Guid NewComb()
{
byte[] guidArray = System.Guid.NewGuid().ToByteArray();
DateTime baseDate = new DateTime(1900,1,1);
DateTime now = DateTime.Now;
// Get the days and milliseconds which will be used to build the byte string
TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
TimeSpan msecs = new TimeSpan(now.Ticks - (new DateTime(now.Year, now.Month, now.Day).Ticks));
// Convert to a byte array
// Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333
byte[] daysArray = BitConverter.GetBytes(days.Days);
byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds/3.333333));
// Reverse the bytes to match SQL Servers ordering
Array.Reverse(daysArray);
Array.Reverse(msecsArray);
// Copy the bytes into the guid
Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
return new System.Guid(guidArray);
}
//================================================================
/// <summary>
/// 從 SQL SERVER 前往的 GUID 中生成時光信息
/// </summary>
/// <param name="guid">包括時光信息的 COMB </param>
/// <returns>時光</returns>
public static DateTime GetDateFromComb(System.Guid guid)
{
DateTime baseDate = new DateTime(1900,1,1);
byte[] daysArray = new byte[4];
byte[] msecsArray = new byte[4];
byte[] guidArray = guid.ToByteArray();
// Copy the date parts of the guid to the respective byte arrays.
Array.Copy(guidArray, guidArray.Length - 6, daysArray, 2, 2);
Array.Copy(guidArray, guidArray.Length - 4, msecsArray, 0, 4);
// Reverse the arrays to put them into the appropriate order
Array.Reverse(daysArray);
Array.Reverse(msecsArray);
// Convert the bytes to ints
int days = BitConverter.ToInt32(daysArray, 0);
int msecs = BitConverter.ToInt32(msecsArray, 0);
DateTime date = baseDate.AddDays(days);
date = date.AddMilliseconds(msecs * 3.333333);
return date;
}
結語
數據庫主鍵在數據庫中占領主要位置。主鍵的拔取戰略決議了體系能否高效、易用。本文比擬了四種主鍵拔取戰略的優缺陷,並供給了響應的代碼處理計劃,願望對年夜家有所贊助。