IDENTITY 列中出现意外空白

Ege*_*soz 19 sql-server identity sql-server-2012

我正在尝试生成从 1 开始并以 1 递增的唯一采购订单编号。我使用此脚本创建了一个 PONumber 表:

CREATE TABLE [dbo].[PONumbers]
(
  [PONumberPK] [int] IDENTITY(1,1) NOT NULL,
  [NewPONo] [bit] NOT NULL,
  [DateInserted] [datetime] NOT NULL DEFAULT GETDATE(),
  CONSTRAINT [PONumbersPK] PRIMARY KEY CLUSTERED ([PONumberPK] ASC)    
);
Run Code Online (Sandbox Code Playgroud)

以及使用此脚本创建的存储过程:

CREATE PROCEDURE [dbo].[GetPONumber] 
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [dbo].[PONumbers]([NewPONo]) VALUES(1);
    SELECT SCOPE_IDENTITY() AS PONumber;
END
Run Code Online (Sandbox Code Playgroud)

在创建时,这工作正常。当存储过程运行时,它从所需的数字开始并以 1 递增。

奇怪的是,如果我关闭或休眠我的计算机,那么下次运行该程序时,序列已提前了近 1000。

见下面的结果:

采购订单编号

你可以看到数字从 8 跃升到了 1002!

  • 为什么会这样?
  • 我如何确保不会像那样跳过数字?
  • 我所需要的只是让 SQL 生成以下数字:
    • a) 保证唯一。
    • b) 增加所需的数量。

我承认我不是 SQL 专家。我是否误解了 SCOPE_IDENTITY() 的作用?我应该使用不同的方法吗?我查看了 SQL 2012+ 中的序列,但微软表示默认情况下不能保证它们是唯一的。

Aar*_*and 27

这是一个已知和预期的问题 - SQL Server 管理 IDENTITY 列的方式在 SQL Server 2012 中发生了变化(一些背景);默认情况下,它将缓存 1000 个值,如果您重新启动 SQL Server、重新启动服务器、故障转移等,它将不得不丢弃这 1000 个值,因为它没有可靠的方法来知道其中有多少实际发布。这在此处记录。有一个跟踪标志可以更改此行为,以便记录每个 IDENTITY 分配*,防止这些特定间隙(但不是回滚或删除间隙);但是,重要的是要注意,这在性能方面可能非常昂贵,因此我什至不打算在这里提及特定的跟踪标志。

* (就我个人而言,我认为这是一个可以用不同方式解决的技术问题,但由于我不编写引擎,因此无法更改。)

要清楚 IDENTITY 和 SEQUENCE 的工作原理:

  • 两者都不能保证是唯一的(您需要在表级别强制执行,使用主键或唯一约束)
  • 两者都不能保证是无间隙的(例如,任何回滚或删除都会产生间隙,尽管存在此特定问题)

唯一性很容易实现。避免差距不是。您需要确定避免这些差距对您来说有多重要(理论上,您根本不应该关心差距,因为 IDENTITY/SEQUENCE 值应该是无意义的代理键)。如果它非常重要,那么您不应该使用任何一种实现,而是使用您自己的可序列化序列生成器(请参阅此处此处此处的一些想法)- 请注意,它会杀死并发。

关于这个“问题”的很多背景:

  • 你为什么不给他们一个非常简单的报告,只使用 ROW_NUMBER() OVER (PARTITION BY Month ORDER BY ID)?同样,ID 号应该毫无意义,这是一种查看订单数量的可怕方式。如果您的代码中存在删除 1000 行或回滚 275 个交易或 500 个订单被合法取消的错误怎么办? (3认同)
  • @Ege:“...只需查看订单号即可知道有多少”。您的用户将会失望。身份值根本不是这样工作的,你(或他们)也不应该做出任何这样的假设。独特的?是的。连续的?否。计算一个月内提交的采购订单的正确方法是...根据每条记录中的某些[不可更改]日期字段来计算该月内提出的采购订单数量。 (2认同)