【问题标题】:Why are there gaps in my IDENTITY column values?为什么我的 IDENTITY 列值中存在空白?
【发布时间】:2013-01-16 12:17:54
【问题描述】:

我有问题。

我的主 ID (IDENTITY) 配置为自动递增(类型:int)。但是,当我插入一个新行时,这个新的 id 不是连续的。怎么了?有什么解决办法吗?

已编辑:

[...]
[id]int] IDENTITY(1,1) NOT NULL,
[...]
CONTRAINT [PK_Medida] PRIMARY KEY CLUSTERED
(
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

【问题讨论】:

  • 显示你的表结构,
  • 请告诉我们为什么这些差距对您来说是个问题。
  • 这就像问“我的狗在吠叫。有什么问题?”。它是一只狗,它应该会吠叫。这是它的自然行为。它是一个身份列。它只是一个抽象的数字,不保证是连续的。这是它的自然行为。它不连续到底有什么问题?
  • 差距是个问题,但我认为 id 的值会超出范围。我的计算是这个表会非常非常长(数十亿行)
  • 那么 INT 肯定不是正确的数据类型

标签: sql sql-server


【解决方案1】:

我有一个带有一些约束的表,我希望这些约束会导致大量插入语句失败。它在我的由 Identity(1,1) 处理的索引中造成了巨大的空白。

我设计的解决方案是创建一个没有 ID 列的临时表,但包含该表的所有其他列。然后,我指定一个触发器在暂存表表上运行,插入成功后,记录将传输到带有索引的实际表中。在这种情况下,ID 保留在不同的时间完成,并允许将所有值组合在一起用于 ID。

我知道这似乎有点低效,但到目前为止对我来说效果很好。

【讨论】:

    【解决方案2】:

    您可以通过在执行增量语句之前评估预期错误,或使用事务避免此错误,这样语句就永远不会执行并在有任何错误。希望对你有帮助

    【讨论】:

    • 在具有标识列的表上回滚插入不会将标识值回滚到前一个值。如果您不执行插入,则使用事务仅保存标识值。发出 INSERT 后,除非您明确告诉 SQL 重新设置为您刚刚浪费的数字,否则该标识值将被消耗。
    【解决方案3】:

    差距出现在以下情况:

    1. 记录被删除。
    2. 尝试插入 新记录(例如,非空约束错误)。标识值为 无奈地跳过。
    3. 有人用显式插入/更新了它 值(例如 identity_insert 选项)。
    4. 增量值大于 1。

    【讨论】:

    • 5 。为字段设置了“默认值或绑定”(例如 getdate()) - 导致字段在创建行后更新
    • 对我来说,数据库日志已满,实体框架出现 DBUpdateExceptions。
    【解决方案4】:

    The identity property on a column does not guarantee the following

    值的唯一性 – 必须通过使用 PRIMARY KEY 或 UNIQUE 约束或 UNIQUE 索引来强制执行唯一性。

    事务中的连续值 – 插入多行的事务不能保证获得行的连续值,因为表上可能会发生其他并发插入。如果值必须是连续的,那么事务应该在表上使用排他锁或使用 SERIALIZABLE 隔离级别。

    服务器重新启动或其他故障后的连续值 - SQL Server 可能出于性能原因缓存标识值,并且某些分配的值可能会在数据库故障或服务器重新启动期间丢失。这可能会导致插入时标识值出现间隙。如果间隙不可接受,则应用程序应使用带有 NOCACHE 选项的序列生成器或使用它们自己的机制来生成键值。

    值的重用 – 对于具有特定种子/增量的给定身份属性,引擎不会重用身份值。如果特定的插入语句失败或插入语句回滚,则使用的标识值将丢失并且不会再次生成。这可能会导致生成后续标识值时出现间隙。

    另外,

    如果频繁删除的表存在标识列,则标识值之间可能会出现间隙。如果这是一个问题,请不要使用 IDENTITY 属性。但是,为确保没有创建任何空白或填补现有空白,请先评估现有的身份值,然后再使用SET IDENTITY_INSERT ON 明确输入一个值。

    另外,检查身份列属性并检查身份增量值。它应该是 1。

    【讨论】:

    • +1,但我个人看到重用(导致主键违规)。我从来没有手动插入过一行。我从来没有删除过一行。所有插入都由事务中的一个进程完成(尽管该进程有时会在中间被杀死)。它只是突然停止工作,叹息。
    【解决方案5】:

    不要期望身份是连续的。有许多情况可能会留下空白。将身份视为抽象数字,不要为其附加任何商业意义。

    【讨论】:

    • 是对的,但是我预计我的表会很长,我担心以后的id取值超出范围。
    • 在 MySQL 中,id-autoincrement 总是连续的,在 SQL Server 中为什么不呢?
    • 在 MySQL 中它也可以留下空白。主要原因是刀片回滚,它们会导致间隙。一个直接的插入/提交/插入/提交将创建一个密集的 IDs 系列,没有间隙。但差距总是会出现。如果您有太多的空白需要担心 4 字节有符号 int 地址空间的 ID 耗尽,那么您一定是在代码中做错了事情才能留下这样的空白。最后,如果您真的担心,请使用 bigint 而不是 int
    • 前一个id和最近一个id的“跳跃”很大。我以前的 id 是 1444,新的是 2433。“跳跃”了 1000!
    • 表示有 1000 个插入已回滚。发生这种情况的方式只有大约 1 毫米,没什么大不了的。例如。您测试了一些批处理插入并在调试器中停止/中止。问题是这在实际使用场景中如何工作。我不希望您的主要用例是回滚。
    【解决方案6】:

    新插入的行不再使用被删除行的自动 ID。我无法为您提供解决方案,但这就是行为。

    沃特

    【讨论】:

    • 我没有删除任何行