2009-10-09 68 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