SQL Server 重复使用相同的 IDENTITY Id 两次

3
我希望这个问题不会太泛泛。我有一个表格“Person”,它有一个PK Identity列“Id”。通过C#,我为“Person”插入新条目,并将Id设置为1、2、3,用于添加的3个人。同样通过C#,我执行所有的删除操作,使得表格中没有任何“Person”了。之后,我在表格“Person”上运行一些更改脚本(由于太长,我不能发布它们)。我没有进行任何RESEED。现在问题来了:如果我调用“SELECT IDENT_CURRENT('Person')”,它显示3而不是4。如果我再次插入“Person”,我将得到一个Id为3的人,而不是Id为4的人。你知道为什么会这样吗?这是怎么发生的?【编辑】我想我找到了我的问题的解释:当通过SQL Server Management Studio执行DB更改时,设计师会创建一个临时表Tmp_Person,并将数据从Person移动到其中。之后,他将Tmp_Person重命名为Person。由于这是一个新表格,索引从头开始。

3
除非你能共享重现此问题的代码,否则这里没有任何可以回答的内容。问题几乎肯定出在“更改脚本(它们太长了,我无法发布)”中。 - Sean Lange
@SeanLange 是的,我想我找到了问题所在。请看我的修改。 - gsharp
看来你已经回答了。 - Sean Lange
猜测您是使用GUI在表格中间添加列,@gsharp,而不是使用ALTER语句将其添加到末尾?使用GUI很有用,但它可能并不总是完全符合您的预期。 - Thom A
1个回答

2
一个IDENTITY属性并不保证唯一性。这就是PRIMARY KEYUNIQUE INDEX的作用。文档中在备注部分涵盖了这一点,以及其他预期行为。CREATE TABLE (Transact-SQL) IDENTITY (Property) - Remarks:
在列上的标识属性不能保证以下内容:
  • 值的唯一性 - 必须使用PRIMARY KEY或UNIQUE约束或UNIQUE索引来强制执行唯一性。
  • 事务中连续的值 - 插入多行的事务不能保证为行获得连续的值,因为表上可能发生其他并发插入。如果值必须是连续的,则事务应锁定表或使用SERIALIZABLE隔离级别。
  • 服务器重新启动或其他故障后的连续值 - SQL Server可能会缓存标识值以提高性能,并且在数据库故障或服务器重新启动期间可能会丢失一些分配的值。这可能导致插入时标识值中存在间隙。如果不接受间隙,则应用程序应使用自己的机制生成关键值。使用带NOCACHE选项的序列生成器可以将间隙限制为从未提交的事务。
  • 值的重复使用 - 对于具有特定种子/增量的给定标识属性,引擎不会重复使用标识值。如果特定的插入语句失败或插入语句回滚,则使用过的标识值将丢失,并且不会再次生成。当生成后续标识值时,可能会出现间隙。
这些限制是设计的一部分,以提高性能,并且因为它们在许多常见情况下是可以接受的。如果由于这些限制无法使用标识值,请创建一个单独的表来保存当前值,并使用应用程序管理对表和编号分配的访问。
这个问题中我加粗了重点。


1
我写了PK Identity列 :) - gsharp
2
但它确保“引擎不会重复使用标识值”,而且问题说明他们看到了两次“3”。 - Martin Smith
然后看一下关于“值的重用”的后一点,@gsharp。IDENTITY不会有意地重用一个值,但这并不意味着它不会(例如,如果某人重新设置表的种子)。如果你想阻止ID被重用,那么不要从表中删除行;使用某种列来表示该行不再是“活动的”,并相应地编写查询语句。 - Thom A
1
@MartinSmith,IDENTITY并不能真正阻止值的重复使用;如果发出了RESEED命令,甚至可以在同一张表中有两行具有相同的ID。它不会自动“回退”并重新使用先前的值,这是确实的,但它无法阻止某人“强制”这样做。这很可能意味着原帖作者或其他人已经对表进行了重新种子化操作。 - Thom A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接