溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

sqlserver數(shù)據(jù)庫(kù)主鍵的生成方式有哪些

發(fā)布時(shí)間:2021-12-01 14:12:57 來(lái)源:億速云 閱讀:228 作者:柒染 欄目:數(shù)據(jù)庫(kù)

sqlserver數(shù)據(jù)庫(kù)主鍵的生成方式有哪些,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

主鍵的生成方式主要有三種: 

一. 數(shù)據(jù)庫(kù)自動(dòng)生成 

二. GUID 

三. 開發(fā)創(chuàng)建 

嚴(yán)格講這三種產(chǎn)生方式有一定的交叉點(diǎn),其定位方式將在下面進(jìn)行講解。 

第一種方式,主要將其定位在自增長(zhǎng)的標(biāo)識(shí)種子:可以設(shè)置起始數(shù)值,及增長(zhǎng)步長(zhǎng)。其優(yōu)點(diǎn)在于使用時(shí)完全將并發(fā)任務(wù)交于數(shù)據(jù)庫(kù)引擎管理,你不用擔(dān)心存在多用戶使用的時(shí)候會(huì)產(chǎn)生兩個(gè)相同的ID的情況。其缺點(diǎn)也在于此,多數(shù)的數(shù)據(jù)庫(kù)不提供直接獲取標(biāo)識(shí)ID的方式,對(duì)于開發(fā)人員來(lái)說(shuō)產(chǎn)生ID的方式是透明的,開發(fā)人員幾乎無(wú)法干預(yù)此項(xiàng)。對(duì)于數(shù)據(jù)的遷移也不是很方便。 由于存在上面的利弊,這種自增長(zhǎng)的ID一般多用于設(shè)計(jì)基礎(chǔ)表(系統(tǒng)運(yùn)行的基礎(chǔ)信息,如員工表)主鍵,而極少(根本不)用于主從表主、外鍵,因?yàn)樵诋a(chǎn)生主從表數(shù)據(jù)并關(guān)聯(lián)時(shí),必須確定主表的ID,然后才能定位從表的關(guān)聯(lián)ID。

 例(MsSQL):

 代碼如下: 

 --創(chuàng)建測(cè)試表 CREATE TABLE [Identity]( Id INT IDENTITY(1,2) NOT NULL PRIMARY KEY,

--種子的起始值1,步長(zhǎng)2 Number VARCHAR(20) UNIQUE NOT NULL, Name VARCHAR(20) NOT NULL, Password VARCHAR(20) DEFAULT(123), Description VARCHAR(40) NULL ) 

--插入記錄 INSERT INTO [Identity](Number,Name,Description) VALUES('001','1st','Id=1,因?yàn)槠鹗贾?') 

INSERT INTO [Identity](Number,Name,Description) VALUES('002','2nd','Id=3,因?yàn)槠鹗贾?,步長(zhǎng)2') 

INSERT INTO [Identity](Number,Name,Description) VALUES('003','3rd','Id=5,由于字符長(zhǎng)度超長(zhǎng),報(bào)錯(cuò)插入失敗,造成此Id產(chǎn)生后被放棄') 

INSERT INTO [Identity](Number,Name,Description) VALUES('004','4th','Id=7 not 5,因?yàn)榈谌龡l記錄插入失敗') 

--檢索記錄,查看結(jié)果 SELECT * FROM [Identity]

結(jié)果: (1 行受影響) (1 行受影響) 消息 8152,級(jí)別 16,狀態(tài) 14,第 3 行 將截?cái)嘧址蚨M(jìn)制數(shù)據(jù)。 語(yǔ)句已終止。 (1 行受影響) (3 行受影響) Id Number Name Password Description 1 001 1st 123 Id=1,因?yàn)槠鹗贾? 3 002 2nd 123 Id=3,因?yàn)槠鹗贾?,步長(zhǎng)2 7 004 4th 123 Id=7 not 5,因?yàn)榈谌龡l記錄插入失敗 第二種方式,GUID即Globally Unique Identifier,也稱為UUID(Universally Unique IDentifier),全球唯一標(biāo)識(shí)符,GUID一般由32位十六進(jìn)制的數(shù)值組成,其中包含網(wǎng)卡地址、時(shí)間及其他信息。任何兩臺(tái)電腦都不會(huì)產(chǎn)生相同的GUID,他的優(yōu)點(diǎn)在唯一性,當(dāng)需要數(shù)據(jù)庫(kù)整合時(shí),能節(jié)約不少勞動(dòng)力。比如總公司和分公司各自系統(tǒng)獨(dú)立運(yùn)行,所有分公司數(shù)據(jù)定期需要提交到總部,可以避免合并數(shù)據(jù)時(shí)主鍵沖突問(wèn)題,同時(shí)GUID還兼具自增長(zhǎng)標(biāo)識(shí)種子特點(diǎn),無(wú)需開發(fā)人員太多的關(guān)注。但是GUID信息量大,占用空間也大,關(guān)聯(lián)檢索時(shí),估計(jì)效率上也不是很高,對(duì)于32位的十六進(jìn)制其可讀性也差,雖然主鍵有對(duì)用戶的無(wú)意義性,但是在設(shè)計(jì)或者調(diào)試交流時(shí)很不方便。 從長(zhǎng)遠(yuǎn)考慮,為了保證數(shù)據(jù)的可移植性,一般還是會(huì)選擇使用GUID來(lái)作為主鍵。 

例(MsSQL):

 代碼如下:  

--創(chuàng)建測(cè)試表 CREATE TABLE GUID( Id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,

--當(dāng)然你也可以用字符串來(lái)保存 Number VARCHAR(20) UNIQUE NOT NULL, Name VARCHAR(20) NOT NULL, Password VARCHAR(20) DEFAULT(123) ) 

--插入記錄 INSERT INTO GUID(Id,Number,Name) VALUES(NewID(),'001','1st') 

INSERT INTO GUID(Id,Number,Name) VALUES(NewID(),'002','2nd')

INSERT INTO GUID(Id,Number,Name) VALUES(NewID(),'003','3rd') 

--檢索記錄,查看結(jié)果 SELECT * FROM GUID

結(jié)果: Id Number Name Password 8E194F55-B4D3-4C85-8667-33BC6CD33BBC 001 1st 123 7141F202-7D0E-4992-9164-5043EC9FC6F6 002 2nd 123 E0E365A0-8748-4656-AF24-5D0B216D2095 003 3rd 123 第三種方式開發(fā)創(chuàng)建,其便捷性在于可控制性,此可控制性是指其組成形式,可以是整形、也可以是字符型,你可以根據(jù)實(shí)際情況給予多樣的組成及產(chǎn)生形式,說(shuō)到這里可能有的朋友就想起來(lái)自動(dòng)產(chǎn)生單號(hào),如:20120716001或者PI-201207-0001等等,沒(méi)錯(cuò),自我創(chuàng)建同樣適用于這些類似的應(yīng)用。 說(shuō)到自我創(chuàng)建,多數(shù)首先想到的是取Max(Id)+1,這種方式雖然省事,但是實(shí)際上對(duì)于定制(在生產(chǎn)單號(hào)之類的有一定意義的信息時(shí)可能會(huì)有這樣的需求,主鍵沒(méi)必要)及并發(fā)的處理并不是很好。如,當(dāng)前表中最大編號(hào)為1000,當(dāng)C1和C2用戶同時(shí)取這個(gè)Id處理時(shí),得到的都是1001,導(dǎo)致保存失敗。常規(guī)的做法是在取值時(shí)候加鎖,但是當(dāng)多用戶頻繁操作時(shí),性能是個(gè)很大的問(wèn)題,其中主要的原因之一是直接操作的業(yè)務(wù)數(shù)據(jù)表。 針對(duì)此種情況,解決方案是使用鍵值表來(lái)保存表名、當(dāng)前或者下一個(gè)Id及其他信息,如果系統(tǒng)中多個(gè)表Id都使用這種方式,那么鍵值表中就會(huì)有多條相應(yīng)的規(guī)則記錄;當(dāng)然也可以讓整個(gè)數(shù)據(jù)庫(kù)所有表的Id從都按相同的規(guī)則從一個(gè)源產(chǎn)生,那么鍵值表中只需要一條規(guī)則記錄即可。 

下面來(lái)看看這樣一個(gè)使用鍵值表例子的演變(MsSQL):

代碼如下: 

 --創(chuàng)建鍵值表 CREATE TABLE KeyTable( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, 

TCode VARCHAR(20) UNIQUE NOT NULL, 

TName VARCHAR(50) NOT NULL, 

TKey INT NOT NULL, ) 

GO 

--插入測(cè)試記錄 INSERT INTO KeyTable(TCode,TName,TKey) VALUES('T001','Test',0) GO 

--創(chuàng)建獲取指定表ID的存儲(chǔ)過(guò)程,也可以修改成函數(shù) CREATE PROCEDURE UP_NewTableID @TCode VARCHAR(20),@NextID INT OUTPUT AS DECLARE @CurTKey INT,

@NextTKey INT BEGIN TRAN TransID SELECT @CurTKey=TKey FROM KeyTable

 WHERE TCode = @TCode IF @@ROWCOUNT = 0 BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is exists',16,1) RETURN END SET 

@NextTKey = @CurTKey + 1 --WAITFOR DELAY '00:00:05' UPDATE KeyTable SET TKey = @NextTKey 

WHERE TCode = @TCode IF @@ROWCOUNT = 0 BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is updated',16,1) RETURN END COMMIT TRAN TransID SET @NextID = @NextTKey GO

執(zhí)行存儲(chǔ)過(guò)程UP_NewTableID

 代碼如下: 

 DECLARE @NextID INT EXEC UP_NewTableID 'T001',

@NextID OUTPUT PRINT

@NextID運(yùn)行的時(shí)會(huì)發(fā)現(xiàn)很正常,獲取的結(jié)果也很正確。但是如果在高并發(fā)的情況,多個(gè)用戶可能就會(huì)獲取相同的ID,如果獲取的ID后是用于保存對(duì)應(yīng)表中的記錄,那么最多只有一個(gè)用戶能保存成功。 

下面模擬一下并發(fā)情形,將上面的存儲(chǔ)過(guò)程UP_NewTableID中語(yǔ)句WAITFOR DELAY '00:00:05'的注釋去掉,打開3個(gè)查詢分析器的窗體,依次執(zhí)行上面語(yǔ)句。 預(yù)期是想分別獲得1,2,3,但是也許會(huì)發(fā)現(xiàn)多個(gè)窗體的運(yùn)行結(jié)果都是:1。這就是說(shuō)在更新語(yǔ)句執(zhí)行之前,大家都獲取的ID是0,所以下一個(gè)數(shù)值都是為1。(實(shí)際的數(shù)值,根據(jù)DELAY的參數(shù)大小及運(yùn)行時(shí)間按間隔有關(guān)) 從這方面來(lái)分析的話有的朋友可能就會(huì)想到,是否可以在更新語(yǔ)句執(zhí)行時(shí)判斷ID是不是原始ID了?

修改過(guò)程:

代碼如下: ALTER PROCEDURE UP_NewTableID @TCode VARCHAR(20),

@NextID INT OUTPUT AS DECLARE @CurTKey INT,

@NextTKey INT BEGIN TRAN TransID SELECT 

@CurTKey=TKey FROM KeyTable WHERE TCode=@TCode IF @

@ROWCOUNT=0BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is exists',16,1) RETURN END SET @NextTKey=@CurTKey+1 WAITFOR DELAY '00:00:05' UPDATE KeyTable SET TKey=@NextTKey WHERE TCode=@TCode AND TKey=@CurTKey--此處加上TKey的校驗(yàn) IF @@ROWCOUNT=0BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is updated',16,1) RETURN END COMMIT TRAN TransID SET @NextID=@NextTKey GO

如果打開個(gè)3個(gè)執(zhí)行過(guò)程來(lái)模擬并發(fā),那么會(huì)有2個(gè)窗體出現(xiàn): 消息 50000,級(jí)別 16,狀態(tài) 1,過(guò)程 UP_NewTableID,第 28 行 Warning: No such row is updated 由此會(huì)看到還是會(huì)由于并發(fā)導(dǎo)致有用戶操作失敗,但是較上一個(gè)至少將錯(cuò)誤出現(xiàn)的時(shí)間點(diǎn)提前了。 那么有沒(méi)有更好的方法,從查詢到更新結(jié)束整個(gè)事務(wù)過(guò)程中,不會(huì)有任何其他事務(wù)插入其中來(lái)攪局的辦法呢,答案很明確,有,使用鎖!需要選擇適當(dāng)?shù)逆i,否則效果將和上面的一樣。

 代碼如下: 

 ALTER PROCEDURE UP_NewTableID 

@TCode VARCHAR(20),@NextID INT OUTPUT AS DECLARE 

@CurTKey INT,@NextTKey INT BEGIN TRAN TransID SELECT 

@CurTKey=TKey FROM KeyTable WITH (UPDLOCK)

--采用更新鎖,并保持到事務(wù)完成 WHERE TCode=@TCode IF @@ROWCOUNT=0BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is exists',16,1) RETURN END SET 

@NextTKey=@CurTKey+1 WAITFOR DELAY '00:00:05' 

UPDATE KeyTable SET TKey=@NextTKey WHERE TCode=@TCode

--此處無(wú)需驗(yàn)證TKey是否與SELECT的相同 COMMIT TRAN TransID SET @NextID=@NextTKey GO

可以打開N(N>=2)個(gè)窗體來(lái)進(jìn)行測(cè)試,將會(huì)看到所有操作都被串行化,結(jié)果就是我們想要的那樣。如此注釋或者去掉模仿并發(fā)的語(yǔ)句WAITFOR DELAY '00:00:05'即可。 

如前面所說(shuō),這同樣適應(yīng)于單據(jù)編號(hào)類似編碼的產(chǎn)生形式,只要對(duì)前面的代碼及鍵值表稍作修改即可,有興趣的朋友可以一試。如果是從前端取得這個(gè)編號(hào),并應(yīng)用于各個(gè)記錄,那么可能存在跳號(hào)的可能。如果為了保證不存在跳號(hào),一種解決方案就是使用跳號(hào)表,將跳號(hào)記錄定期掃描并應(yīng)用于其他記錄。另一種解決方案是將記錄的保存操作放置到編號(hào)產(chǎn)生的過(guò)程中,形成一個(gè)串行化的事務(wù)。 

關(guān)于sqlserver數(shù)據(jù)庫(kù)主鍵的生成方式有哪些問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI