什么可能导致插入错误的 ID?

DFo*_*k42 7 sql-server-2008 sql-server identity

我有一个 SQL Server 2008 服务器(版本 10.0.5500)。本周早些时候,我在一个已经有数据的表上运行了这个:

delete from dbo.table
go    
dbcc checkident('dbo.table',reseed,0)
Run Code Online (Sandbox Code Playgroud)

当用户稍后创建新记录时,不知何故将 ID 0 插入到 ID 列中,而不是 SQL Server 通常放入的 1,如果为 ID 配置了 identity(1,1)。

这导致了一些奇怪的问题,但正如预期的那样,清除数据并运行重新种子会导致插入 1。我无法复制这个问题。

作为参考,这里是我们的保存 sp 的一般格式:

alter procedure dbo._TableSave
    @pk_id int,
    @field varchar(50)
as
    if (@pk_id is null)
    begin
        set nocount on;

        insert into dbo.Table
        (
            Field
        )
        values
        (
            @field
        );
        select scope_identity();
    end
    else
    begin
        update dbo.Table
        set Field=@field
        where PK_ID=@pk_id

        select @pk_id
    end
Run Code Online (Sandbox Code Playgroud)

有谁知道什么会导致 SQL Server 在 ID 应该是 1 的时候在 ID 中插入 0?

我们不会在应用程序的任何位置将数据插入到标识列(打开identity_insert)。

Han*_*non 10

这样做dbcc checkident('dbo.table',reseed,0)将导致新创建/截断表中的下一个条目的标识为 0。

        CREATE TABLE TestIdent
        (
            ID INT NOT NULL CONSTRAINT PK_TestIdent PRIMARY KEY CLUSTERED IDENTITY(1,1)
            , SomeText nvarchar(255)
        );

        dbcc checkident('dbo.TestIdent',reseed,0)

        INSERT INTO TestIdent VALUES ('Test');

        SELECT * FROM dbo.TestIdent;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

这会导致输入的下一个身份为 10:

        TRUNCATE TABLE dbo.TestIdent;

        DBCC CHECKIDENT('dbo.TestIdent',reseed,10)

        INSERT INTO TestIdent VALUES ('Test');

        SELECT * FROM dbo.TestIdent;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • 仅当表是新创建的或已被截断时。如果插入一行并删除它,则会得到不同的结果 `CREATE TABLE dbo.[table] (id int identity(1,1));INSERT INTO dbo.[table] DEFAULT VALUES;DELETE FROM dbo.[table]; dbcc checkident('dbo.table',reseed,0);INSERT INTO dbo.[table] 输出已插入。* DEFAULT VALUES;DROP TABLE dbo.[table]` (4认同)

Roi*_*ish 9

我建议使用以下代码来防止它发生:(
而不是您在问题中发布的代码)

DELETE FROM dbo.table
GO
IF EXISTS 
(
    SELECT * 
    FROM sys.identity_columns 
    WHERE object_id = OBJECT_ID('dbo.table') 
        AND last_value IS NOT NULL
)
DBCC CHECKIDENT ('dbo.table', RESEED, 0);
Run Code Online (Sandbox Code Playgroud)

它应该防止“重新播种到 0”。

解释:(
我没有找到支持它的官方文档......)
问题出sys.syscolpars在名为列的表中idtval
,该列包含所有标识列信息。
由于该表具有用于在数据库中(不仅标识列)的各列的行,
一个NULL在该领域的值将表示非标识列
(不像sys.identity_columns它使用NULL一个新的\截断表)。

sys.syscolpars使用不同的方法来保存信息:
它使用0x01000000010000000100000001格式(新创建的identity(1,1) 的示例),
左边的8 位数字(在 之后的位置1-8 0x)为Last_Value
接下来的8 位数字(位置9-16后0x)的increment_value
接下来的8位(17-24位后0x)为seed_value
从右侧第二位是一个谜给我,
并从右侧的第1位-告诉我们,如果它是一个新的表(= 1 ) 或不!

回到我们的问题-
DBCC CHECKIDENT运行时,它检查sys.identity_columns.last_value
如果是NULL那么它更新的新值sys.syscolpars.idtval1在正确的位置。
sys.identity_columns.last_value仍然NULL
这会导致下一个插入标识值被视为在新表中。
(我假设sys.identity_columns是独立的,sys.syscolpars因为第一个是立即更新的,而后者仅在执行检查点后更新)。

由于没有sys.syscolpars实时查询的方式(需要DAC),
我的建议是上面的条件,可能再加上ofELSE子句。

再说一次,我不能保证任何这种解释都是正确的,但它确实有效:)

如果有人有更准确的DBCC CHECKIDENT后台操作信息,
我很想学习,请分享!

祝你好运,
??????