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



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

我使用这个代码:

SELECT IDENT_CURRENT('table_name') + 1

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

我认为您需要寻找另一种方法来计算下一个可用值(例如将列设置为自动递增)。

来自IDENT_CURRENT文档,关于空表:

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

它甚至看起来都不那么可靠,尤其是如果你最终设计了一个同时有多个人在表上写字的应用程序。

在使用IDENT_CURRENT预测下一个生成的标识值时要小心。实际生成的值可能不同于IDENT_CURRENT加上IDENT_INCR,因为其他会话执行插入。

如果表为空,则此查询将完美工作。

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

我知道已经有了答案,但我真正感到恼火的是,我所有的搜索都是按照"获取下一个身份sql server"的思路进行的,结果都是不稳定的解决方案(比如只选择当前身份值并加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样本使用了这种技术(或至少暗示了这一点),作为强制实体始终具有有效标识符的一种方式。如果您的实体在被INSERT插入数据库之前有一个伪标识符(如-1default(int)null),则它可能会泄漏持久性问题。

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

我倾向于同意其他海报的观点,即这不是正确的方法,但在某些情况下它可能很方便。有几个帖子问我为什么要这样做,让我给你举一个例子,在哪里对我方便,以及如何以及为什么这样做。

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

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

有些区块包含数千笔交易。一些交易包含大量的输入和/或输出。我需要通过C#中的一个方便调用将它们存储在DB中,而不会损害完整性(ID都连接起来)和良好的性能——当有接近10000次提交时,我无法通过逐行提交来获得这些性能。

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

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

这个解决方案可能并不优雅,可以说可能不符合书本,但它既方便又简单。我知道,还有其他方法可以实现这一点,但这只是直截了当的,需要几个小时才能实现。只要确保您没有并发问题。

而不是询问"你为什么要这么做&";,我认为你有充分的理由。

以下是我为一些快速而肮脏的测试SQL所做的,它在中运行良好,只有一个除外,即使在清空之前填充的表之后也是如此:

DECLARE @nextId INTEGER = IDENT_CURRENT('myTable')
IF (SELECT COUNT(*) FROM myTable) > 0
OR @nextId > 1
SET @nextId += 1

然后,我用它来构建一个状态字符串,并将其放入myTable中(我希望它具有该行将要给定的NEW标识值),以验证SQL是否按预期工作。

只有的情况不能正确处理:

  1. 创建一个新的(空的)表并运行上面的代码。它将正确地告诉您它将具有ID 1
  2. 单个元素添加到表中。正如预测的那样,它的ID将为1
  3. 清空桌子
  4. 再次运行上述SQL。它会错误地告诉您下一个条目的ID也将为1

这是我找不到解决方法的一个案例。我对此很满意,因为我的测试的第1步涉及到向表中添加多行,但要注意这是一个不太可能但可能导致错误的事件序列。

可能存在一个";"更好";是的,但它不适用于生产代码,所以快速和肮脏是可以的。

我可以提供另一种情况,在这种情况下,知道下一个值会很有用。我在Excel中的一行中有数据,例如:A B C X1 X2 Y1 Y2 Z1 Z2我想把A B C放在一个数据库表中,并在另一个表中使用该记录的新主键,我要放X1和X2,然后放Y1和Y2,然后放Z1和Z2。因此,这一行将在第一个表中生成一条记录:xxx A B C

第二张表中有三条记录

yyy xxx X1 X2

yyy+1 xxx Y1 Y2

yyy+2 xxx Z1 Z3

如果我可以在开始处理之前确定xxx,我可以为正在处理的每个记录递增它。我也犯了一个错误,只是找到了最高值M,并假设M+1将是下一个。这不起作用,因为一些上传由于错误而没有完成,事务被回滚,但索引的PK已经分配,不会被重用。手动删除价值最高的PK记录也会导致类似的问题。

我简单化的解决方案只是编写一个SP,在上传第一个案例后获得PK的最大值。

CREATE     PROCEDURE [MIRR].[GetLast_CasesIndexID]
@GroupNumber as int Output
AS
BEGIN
SELECT @GroupNumber = max(CasesIndexID)  
FROM MIRR.Cases
END
GO

我确实用这种方式计算下一个id,当表在空中时,它也很好

select isnull(max(ID),0)+1 from yourTable

事实上,回滚插入的能力(不回滚分配给值的identity()值)会导致下一个identity值"跳转"。。。因此,将主键视为顺序值是不正确的——您的假设是在执行插入之前没有回滚!它是一个递增的值,以确保值的唯一性。不能保证没有差距。。。即使在开始插入时也是如此。

我建议,当你认为你需要下一个IDENTITY值时,你真的需要一个行上不同表的外键,这样你的集合才能被识别。

此外,您可能希望研究OUTPUT INTO,以捕获插入了哪些记录,从而链接到您插入的信息。。。

INSERT INTO #A(Col1)
OUTPUT inserted.IdentityColumn, inserted.Col1 
INTO #B (DestinationPrimaryKey, SourceRowId)
SELECT SourceRowID FROM #ThingToRecord
SELECT DestinationPrimaryKey, SourceRowId
FROM #B

最新更新