如何从SQL Server获取下一个标识值

我需要从SQL Server获取下一个标识值。

我用这个代码:

 SELECT IDENT_CURRENT('table_name') + 1 

这是正确的,但是当table_name为空(并且下一个标识值为“1”)时返回“2”但结果为“1”

我想你会想要另一种方法来计算下一个可用值(例如将列设置为自动递增 )。

从IDENT_CURRENT文档中,关于空表:

当IDENT_CURRENT值为NULL(因为表从未包含行或已被截断)时,IDENT_CURRENT函数返回种子值。

它甚至看起来都不可靠,特别是如果你最终设计的应用程序有多个人同时写入桌面。

请谨慎使用IDENT_CURRENT来预测下一个生成的标识值。 由于其他会话执行的插入,实际生成的值可能与IDENT_CURRENT加IDENT_INCR不同。

我倾向于同意其他海报,认为这不是正确的方法,但对某些情况来说这可能很方便。 几个post问为什么要这样做,让我举个例子,它对我来说很方便,以及如何以及为什么。

我正在实施一个比特币节点。 我希望区块链存储在SQL数据库中。 每个块都是从其他节点和矿工的网络接收的。 你可以在其他地方找到的细节。

当接收块时,它包含一个头,任意数量的事务和每个事务的任意数量的输入和输出。 我的数据库中有4个表 – 你猜对了 – 表头,事务表,输入表和输出表。 事务中的每一行,输入和输出表都与ID相互链接到标题行。

一些块包含数千个事务。 一些交易包括投入和/或产出。 我需要将它们存储在数据库中,方便调用C#而不影响完整性(所有ID都链接起来)并具有良好的性能 – 当接近10000次提交时,我无法通过逐行提交来获取它们。

相反,我绝对确保在操作期间在C#中同步锁定我的数据库对象(我也不必担心其他进程访问数据库),所以我可以方便地在所有4个表上执行IDENT_CURRENT,返回来自存储过程的值,填充4 List 中的近10000行,同时递增ID并调用SqlBulkCopy.WriteToServer方法并设置选项SqlBulkCopyOptions.KeepIdentity,然后将其全部发送到4个简单调用中,每个表集一个。

性能提升(在4-5岁的中档笔记本电脑上)从大约60-90秒下降到真正大块的2-3秒,所以我很高兴了解IDENT_CURRENT()。

解决方案可能并不优雅,可能不会出书,但它既方便又简单。 我知道,还有其他方法可以实现这一目标,但这只是直截了当,花了几个小时来实现。 只是确保您没有并发问题。

如果您的表将为空,则此查询将完美地运行。

 SELECT CASE WHEN (SELECT COUNT(1) FROM tablename) = 0 THEN 1 ELSE IDENT_CURRENT('tablename') + 1 END AS Current_Identity; 

我知道已经有了答案,但我真的很烦我所有我的搜索都是“获取下一个身份sql服务器”提出了片状解决方案(比如只选择当前的身份值并添加1)或“它可以” “可靠地完成”。

有几种方法可以实现这一点。

SQL Server> = 2012

 CREATE SEQUENCE dbo.seq_FooId START WITH 1 INCREMENT BY 1 GO CREATE TABLE dbo.Foos ( FooId int NOT NULL DEFAULT (NEXT VALUE FOR dbo.seq_FooId) PRIMARY KEY CLUSTERED ) GO // Get the next identity before an insert DECLARE @next_id = NEXT VALUE FOR dbo.seq_FooId 

SQL Server 2012引入了SEQUENCE对象。 在这种情况下,每次调用NEXT VALUE FOR时序列都会递增,因此您不必担心并发。

SQL Server <= 2008

 CREATE TABLE dbo.Foos ( FooId int NOT NULL IDENTITY (1, 1) PRIMARY KEY CLUSTERED ) GO // Get the next identity before an insert BEGIN TRANSACTION SELECT TOP 1 1 FROM dbo.Foos WITH (TABLOCKX, HOLDLOCK) DECLARE @next_id int = IDENT_CURRENT('dbo.Foos') + IDENT_INCR('dbo.Foos'); DBCC CHECKIDENT('dbo.Foos', RESEED, @next_id) COMMIT TRANSACTION 

您可能希望将所有这些封装在存储过程中,尤其是因为DBCC语句需要提升访问权限,并且您可能不希望每个人都具有这种访问权限。

并不像NEXT VALUE FOR那样优雅,但它应该是可靠的。 请注意,如果表中没有行,则第一个值将获得2 ,但如果您打算始终使用此方法获取下一个标识,则可以将标识置于0而不是1 (使用IDENTITY (0, 1) )如果你在1开始时设置为死。

为什么有人想要这样做?

我不能代表问题海报发言,但是“领域驱动设计”和“官方”DDD样本这本书使用了这种技术 (或至少暗示了它),作为强制实体始终拥有有效标识符的一种方式。 如果您的实体具有伪标识符(例如-1default(int)null ),直到它被INSERT到数据库中,它可能会泄漏持久性问题。

 SELECT isnull(IDENT_CURRENT('emp') + IDENT_INCR('emp'),1)