【问题标题】:SQL identity (1,1) starting at 0SQL 标识 (1,1) 从 0 开始
【发布时间】:2014-03-05 14:00:43
【问题描述】:

我有一个带有标识集的 SQL 表:

CREATE TABLE MyTable(
    MyTableID int IDENTITY(1,1) NOT NULL,
    RecordName nvarchar(100) NULL)

这个表发生了一些事情,导致了奇怪的行为。我需要弄清楚是什么。

当插入发生时:

INSERT MyTable(RecordName) 
VALUES('Test Bug')

SELECT SCOPE_IDENTITY() -- returns 0
SELECT * FROM MyTable   -- displays: 0, 'Test Bug'

这是一个问题,因为此插入上方的代码要求第一个 ID 为 1 - 我无法弄清楚 IDENTITY(1,1) 如何以 0 结尾。

如果(在执行 INSERT 之前)我检查它返回 null 的身份:

DBCC CHECKIDENT (MyTable, NORESEED)

检查身份信息:当前身份值'NULL',当前列值'NULL'。

我知道有几种方法可以解决这个问题;我首先需要知道桌子是如何进入这种状态的?

我知道 CHECKIDENT 返回 null 的唯一方法是如果表刚刚创建,但随后 IDENTITY(1,1) 被兑现并且 INSERT 导致 SCOPE_IDENTITY() 成为 1

或者,如果我强制 -1 作为当前种子(DBCC CHECKIDENT (MyTable, RESEED, -1)SET IDENTITY_INSERT MyTable ON),我可以将 0 作为下一个 ID,但随后检查报告当前 -1 种子(而不是 null) ,所以这不可能是发生的事情。

数据库是如何进入列有IDENTITY(1,1)DBCC CHECKIDENT (MyTable, NORESEED)返回null,但下一个INSERT导致SCOPE_IDENTITY()0的状态?

【问题讨论】:

  • 我假设表 MyTable 上有一个触发器。
  • @HamletHakobyan 该表上没有触发器。
  • 重新建表时是否达到了同样的结果?
  • 如果你关心标识列的数值,那你就用错了。您应该知道的是 a) 它们唯一标识每一行,b) 您可以将它们存储在数字列中。
  • @Damien_The_Unbeliever 我们在代码中进行了优化,其中 ID 必须为 >=1,然后才会产生 DB 调用的开销。这帮助我们避免了大量遗留代码中的 N+1 问题。如果这是在我们从头开始编写的代码库中,这很容易避免。

标签: sql sql-server tsql identity dbcc


【解决方案1】:

我预计某人/某事已经运行:

DBCC CHECKIDENT ('dbo.MyTable', RESEED, 0);

如果您运行以下命令:

CREATE TABLE dbo.MyTable(
    MyTableID int IDENTITY(1,1) NOT NULL,
    RecordName nvarchar(100) NULL
);

DBCC CHECKIDENT ('dbo.MyTable', RESEED, 0);
DBCC CHECKIDENT ('dbo.MyTable', NORESEED);

第二个CHECKIDENT 仍然返回NULL

检查身份信息:当前身份值'NULL',当前列值'NULL'。

但是下一个标识值将是 0。这是 documented behaviour,MSDN 声明:

当前标识值设置为 new_reseed_value。 如果自创建后没有向表中插入任何行,则执行 DBCC CHECKIDENT 后插入的第一行将使用 new_reseed_value 作为标识。否则,插入的下一行将使用 new_reseed_value + 1。如果 new_reseed_value 的值小于标识列中的最大值,则后续引用该表时将生成错误消息 2627。

这仅适用于 sys.identity_columns 中的 last_value 列仍为 NULL 的新创建/截断表。如上所述,如果您要插入一行,将其删除,然后重新设置为 0,则新标识仍为 1。

完整的测试脚本

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
    DROP TABLE dbo.T;

CREATE TABLE dbo.T(ID INT IDENTITY(1,1) NOT NULL);
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
-- OUTPUTS 1

DELETE dbo.T;
DBCC CHECKIDENT ('dbo.T', RESEED, 0);
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
-- OUTPUTS 1

TRUNCATE TABLE dbo.T;
DBCC CHECKIDENT ('dbo.T', RESEED, 0);
INSERT dbo.T OUTPUT inserted.* DEFAULT VALUES;
-- OUTPUTS 0

【讨论】:

  • 干杯!这就是问题所在——有人打电话给DBCC CHECKIDENT (MyTable, RESEED, 0),而他们应该打电话给DBCC CHECKIDENT (MyTable, RESEED)。我不知道如果该表从未被填充过,仍然会报告 null 作为当前标识。
  • @Keith 你的意思是说他们应该打电话给DBCC CHECKIDENT (MyTable, NORESEED)
猜你喜欢
  • 2014-06-22
  • 2010-10-18
  • 1970-01-01
  • 2019-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多