【问题标题】:Why are my SQL Server identity values being reused?为什么我的 SQL Server 标识值被重复使用?
【发布时间】:2011-07-18 20:04:09
【问题描述】:

我有一个表格,用于在将信息发送到另一个系统 (Dynamics CRM) 之前对其进行排队。我将信息写入该队列,然后另一个服务出现并将数据写入 CRM。

我为主键使用了一个标识值,并将此键与信息一起保存,以便在出现任何问题时跟踪它的来源。将数据拉出队列后,我删除了记录。

但是,SQL Server 似乎正在重用身份字段上的键。许多 id 只使用一次,但许多 id 使用了两次,也有不少人使用了 3 次。显然,这使得通过该 id 查找历史记录毫无用处。难道我做错了什么?我认为标识值应该是唯一的,并且表不应该重用它们。

如果有帮助,这里是列的属性。

关于主键属性的更多属性:

【问题讨论】:

  • 你在使用TRUNCATE TABLE声明吗?
  • @Tocco 这是一个很好的问题,我正在使用一个名为“Scribe”的集成服务。我所做的只是选中一个复选框,上面写着“删除源行”我不确定它使用什么 T-SQL 来执行删除。如果它使用的是截断表,那会导致键被重用吗?
  • 是的,TRUNCATE TABLE 重置身份列。您必须使用DELETE 来防止身份值重置。
  • 由乍得“TRUNCATE TABLE 语句将清除身份值...”
  • @Jeff Davis,你能链接那个“抄写员”吗?

标签: sql-server sql-server-2008-r2 identity uniqueidentifier


【解决方案1】:

身份值在需要时在表中是唯一的。

如果您截断表,SQL 引擎将不知道旧的标识值。

如果您想要一个在表的生命周期内只使用一次的值,您应该考虑使用 GUID 或根据身份 + 日期时间创建唯一键。

【讨论】:

  • 在这种情况下,我可以停止删除记录。这不是真的必要,它只是更清洁。然而,这完美地描述了这个问题。
  • 您仍然应该弄清楚为什么 Scribe 坚持要填补您在删除行时创建的空白。这是非常不直观的行为,随着时间的推移,我看不出重用这些无意义的 ID 值的意义。
【解决方案2】:

不确定您如何检索身份(@@identity 等),但不同的方法存在一些与范围相关的限制。

以下是关于 3 种获取身份的方法的(有限但公平的)文章: http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/

【讨论】:

    【解决方案3】:

    IDENTITY 本身并不意味着唯一 - 它只是意味着从当前种子值自动递增增量值。它不关心是否已经存在这样的值。如果您还声明该列是唯一的(在像您这样的情况下,这通常意味着将其标记为主键),您将不会得到任何这些重复项,并且您应该很快找出谁是错误的,因为应用程序或用户将收到约束违规错误。

    除了TRUNCATE TABLE,还有其他方式可以类似地违反 IDENTITY 值:

    DBCC CHECKIDENT() 可用于手动将当前种子值设置为用户想要的任何值。

    SET IDENTITY_INSERT 可用于手动覆盖下一个可用值。 Scribe 可能会这样做,以便他们不必关心您是否将列设置为标识值,他们仍然可以用他们想要的任何内容覆盖它。

    您可以运行服务器端跟踪来捕获这些事件,以查明是否正在调用它们中的任何一个。

    指定您正在使用的 SQL Server 版本总是很有用的,或者至少用您需要支持的最低版本标记问题,例如,SQL Server 2000 的解决方案和行为通常可能相当不同于 SQL Server 2008 R2。只是对未来问题的有用提示。

    【讨论】:

    • 好的。这是 SQL Server 2008 R2。此外,它被设置为主键。专栏还写着“独一无二”| “是的”
    • 那么这个表绝对不可能有两个相同的值。除非你在 SQL Server 中发现了一个世界上没有人发现过的非常大的错误。请注意,随着时间的推移两次看到相同的值与同时看到两次相同的值不同。
    • 好的,你的笔记完美地描述了我所看到的。随着时间的推移,我两次看到相同的值。同一时间两次不同的值。我希望这些价值观永远独一无二,而不仅仅是在排队时独一无二。那一定是我对自己在做什么的误解。
    【解决方案4】:

    TRUNCATE TABLE 语句将清除标识值。也可以通过将IDENTITY_INSERT 设置为ON 来专门插入标识值。

    如果不太了解您的系统是如何设计的,我建议将该标识列设为主键(如果适用)或唯一索引。查看哪些应用程序随后未能更新您的数据库将显示新身份列的来源和来源。

    但是,如上所述,如果您使用 TRUNCATE TABLE 并且只是将这些标识列暴露在其他地方(例如将它们保留在其他表中),那么这是预期的行为。

    如果您要从表中删除所有行并希望不重新设置标识,则必须改用DELETE 命令。见MSDN

    【讨论】:

    • 我做了一些测试,让服务读取并删除了几行。它没有重置身份(至少这次)。系统运行了几个月,好像身份被重置了3、4次,所以不管是什么原因,都不会经常发生。
    【解决方案5】:

    如果您有相关的表,我要做的第一件事就是确保您确实设置了 PK/FK 关系。许多开发人员只是假设应用程序会处理此问题,并在发生此类情况时遇到麻烦。如果表有外键会阻止身份的重用,则不能截断该表。

    如果同一表中同时多次使用标识,则标识不能定义为 PK,除非它是多个字段 PK 的一部分。如果你真的有这种情况,我建议最好的办法是与微软协商,因为发生了一些完全不同的事情。如果人们正在删除旧的并重新使用它们,那就是另一回事了。

    显然有某个进程或某个人出于某种未知原因重置了过去的值。您可以使用 DDL 触发器来记录是否有人更改了 PK、删除外键约束等。至少通过这种方式您可以找到执行此操作的进程。

    【讨论】:

      猜你喜欢
      • 2011-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多