IDENTITY列中出现意外间隔


18

我正在尝试生成从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)    
);

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

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

    INSERT INTO [dbo].[PONumbers]([NewPONo]) VALUES(1);
    SELECT SCOPE_IDENTITY() AS PONumber;
END

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

奇怪的是,如果我关闭计算机或使其进入休眠状态,那么下一次该过程运行时,序列将增加近1000。

查看以下结果:

采购单编号

您会看到数字从8跃升至1002!

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

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

Answers:


25

这是一个已知和预期的问题-SQL Server 2012中IDSQLITY列管理方式的更改(某些背景);默认情况下,它将缓存1000个值,如果重新启动SQL Server,重新启动服务器,进行故障转移等,它将不得不丢弃这1000个值,因为它没有可靠的方法来知道实际上有多少个值发行。这在此处记录。有一个跟踪标志可以更改此行为,以便记录每个IDENTITY分配*,以防止那些特定的间隔(但不能防止回滚或删除引起的间隔);但是,需要特别注意的是,这可能会在性能方面造成很高的代价,因此在这里我什至不会提及特定的跟踪标志。

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

要清楚了解IDENTITY和SEQUENCE如何工作:

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

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

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


该答案(“跟踪标志”部分除外)也适用于大多数其他SQL数据库(无论如何都具有序列)。
mustaccio

感谢您的回答。唯一性是唯一最重要的要求。差距不大,只要不大即可。例如从1到4可以接受,但从4到1003则不可以。
Ege Ersoz

1
简短版本:ID值将用作采购订单号。客户运行月度报告,并希望能够仅通过查看PO编号就可以迅速得知当月提交了多少PO。因此,我们不能使它增加约1000(每周维护一次,所有服务器(包括DB服务器)都将重新启动)。
Ege Ersoz

3
您为什么不给他们一个非常简单的报告,该报告仅使用ROW_NUMBER()OVER(按月分列的订单或按ID分列的订单)?同样,ID号应该没有意义,这是一种可怕的方式来查看已接受的订单数量。如果您的代码中有一个错误,该错误会删除1000行或回滚275个事务,或者合法取消了500个订单,该怎么办?
亚伦·伯特兰

1
@Ege:“ ...仅通过查看采购订单号即可知道多少...”。您的用户将感到失望。身份值根本就行不通,您(或他们)也不应做任何这样的假设。独特?是。连续的?否。对一个月内提交的PO进行计数的正确方法是...根据每个记录中的某些[不可更改的] Date字段来计算该月内提出的PO的数量。
Phill W.

-4

这是SQL Server的问题。您所能做的就是重新填充列。

删除列ID错误的条目。重新设定列标识。然后下一个条目具有正确的ID。

使用以下sql命令重新设置种子身份: DBCC CHECKIDENT ('YOUR_TABLE_NAME', RESEED, 9)-9是最后一个正确的ID


1
“删除条目”是什么意思?
ypercubeᵀᴹ

2
嗯..似乎删除条目可能只会导致数据丢失。
迈克尔·格林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.