2009-10-09 66 views
7

是否有可能在SQL(SQL服務器)之前檢索標識列的下一個ID(整數)在一個表中,並沒有實際,插入行?這不一定是最高的ID加1,如果最近的行已被刪除。獲取下一個ID沒有插入一行

我問這個,因爲我們偶爾需要更新與新行活DB。該行的ID在我們的代碼中使用(如開關(ID){案例ID:},且必須是相同的,如果我們的發展DB和生活DB不同步的,這將是很好的提前預測行ID。部署之前

我當然可以 SET IDENTITY OFF SET INSERT_IDENTITY ON或運行一個事務(這是否回滾ID?)等,但想知道是否有一個函數返回下一個ID(不增加它) 。

+0

事務不會回滾身份計數器,並且總會有一些其他線程使用您認爲稍後會獲得的數字的風險。 – idstam 2009-10-09 11:21:26

+0

感謝此@idstam 我確實懷疑這一點。 – iWeasel 2009-10-09 14:06:59

回答

8

編輯:

花費了一些比較完整的頁面轉儲小時後,我意識到有一個更簡單的方法,我應該的留在動態管理視圖。

值存在備份/恢復,這是一個清楚的指示,它被存儲 - 我傾銷所有頁面的數據庫,並找不到位置/更改時添加記錄。比較200k行轉儲的頁面並不好玩。

我曾經使用過專用的管理控制檯,我把每個暴露的內部表的一個轉儲插入一行,然後進一步轉儲系統表。這兩個轉儲點都是相同的,這表明雖然它存活了,並且因此必須存儲,但即使在該級別也不會暴露。

因此,經過一個圈子後,我意識到DMV確實有答案。

create table foo (MyID int identity not null, MyField char(10)) 
insert into foo values ('test') 
go 10 

-- Inserted 10 rows 
select Convert(varchar(8),increment_value) as IncrementValue, 
    Convert(varchar(8),last_value) as LastValue 
from sys.identity_columns where name ='myid' 


-- insert another row 
insert into foo values ('test') 

-- check the values again 
select Convert(varchar(8),increment_value) as IncrementValue, 
    Convert(varchar(8),last_value) as LastValue 
from sys.identity_columns where name ='myid' 

-- delete the rows 
delete from foo 


-- check the DMV again 
select Convert(varchar(8),increment_value) as IncrementValue, 
    Convert(varchar(8),last_value) as LastValue 
from sys.identity_columns where name ='myid' 

-- value is currently 11 and increment is 1, so the next insert gets 12 
insert into foo values ('test') 
select * from foo 

Result: 
MyID  MyField 
----------- ---------- 
12   test  

(1 row(s) affected) 

只是因爲行得到去除,最後的值沒有重置,所以最後值+增量應該是正確的答案。

也要寫我的博客上的情節。

哦,抄近路到這一切:

select ident_current('foo') + ident_incr('foo') 

所以它實際上原來是很容易 - 但是這一切都假定沒有其他人使用你的ID,而你恢復了過來。很好的調查,但我不想在代碼中使用它。

+0

Yikes! +1對於讓我微笑的答案 – iWeasel 2009-10-09 13:56:01

+0

我花了大半夜的時間研究它,認爲它會爲一個體面的博客項目,並找出它。編輯我的答案。 – Andrew 2009-10-09 21:21:29

+0

@Andrew。我覺得你的手太多了。另一方面,非常感謝。很好的答案。 – iWeasel 2009-10-12 12:16:19

2

而不是使用IDENTITY列,你可以使用UNIQUEIDENTIFIER(GUID)列作爲唯一的行IDENTIFER並插入已知值。

其他選項(我用的)是S ET IDENTITY_INSERT ON,其中所述行ID的源控制的單「文檔」進行管理。

+1

非常好的一點,但Guids在這個特定的桌子上並不是很實用,而且有點太空了。 我現在使用IDENTITY_INSERT。它的工作原理是不知道是否有其他選擇。 :) – iWeasel 2009-10-09 10:40:28

+0

爲Guids +1。 Guids相對於自動增量列的性能會有所降低(儘管他們需要的額外空間並不重要),但它們完全消除了像這樣跳過籃圈的需要。 – MusiGenesis 2009-10-09 12:03:47

+0

如果您使用GUID作爲您的羣集密鑰,則會受到懲罰,然後您在性能和空間方面付出沉重代價。 – Andrew 2009-10-09 15:04:02

2

你可以很容易地確定最後一個值使用是:

SELECT 
    last_value 
FROM 
    sys.identity_columns 
WHERE 
    object_id = OBJECT_ID('yourtablename') 

通常情況下,下一個ID將LAST_VALUE + 1 - 但沒有保證了點。

馬克

+1

準確地說;沒有保證,這就是爲什麼我想知道是否有替代方案。 :) – iWeasel 2009-10-09 10:37:01

+0

不,沒有其他辦法 - SQL Server只會在您實際執行INSERT時真正發出ID - 沒有辦法僞造:-( – 2009-10-09 10:40:34

2

這是有點怪,但它的工作:

如果你想知道下一個值,以獲得最大的價值加上一個開始:

SELECT max(id) FROM yourtable 

要使這項工作,你需要重置插入身份:

DECLARE @value INTEGER 

SELECT @value = max(id) + 1 FROM yourtable 

DBCC CHECKIDENT (yourtable, reseed, @value) 

INSERT INTO yourtable ... 

不是完全是一個完美的解決方案,但我還沒有我的咖啡又;-)

(這還假定有什麼用你的進程或代碼的第一和第二塊之間的任何其他進程進行表)。

+0

這可能不夠高雅,但我會將其標記爲答案你可以相當肯定地說,在你運行這個之前沒有新的行被插入 – iWeasel 2009-10-09 14:09:16

+0

對不起Jeff,@Andrew花費了一整晚的時間進行了很好的編輯工作,一定要重新分配答案 – iWeasel 2009-10-12 12:15:01

7

嘗試IDENT_CURRENT

返回指定表或視圖生成的最後一個標識值:

Select IDENT_CURRENT('yourtablename') 

即使你沒有在當前會話中插入任何行,這工作。生成的最後一個標識值可以用於任何會話和任何範圍。

+1

不錯。這會獲取最新使用的值,MSDN頁面會提醒您「使用IDENT_CURRENT預測下一個生成的標識值時要謹慎。實際生成的值可能與IDENT_CURRENT加上IDENTITY_SEED不同,因爲其他會話執行的插入操作」。 – PaulG 2009-10-09 11:27:28