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)
我建议使用以下代码来防止它发生:(
而不是您在问题中发布的代码)
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.idtval
与1
在正确的位置。
sys.identity_columns.last_value
仍然NULL
。
这会导致下一个插入标识值被视为在新表中。
(我假设sys.identity_columns
是独立的,sys.syscolpars
因为第一个是立即更新的,而后者仅在执行检查点后更新)。
由于没有sys.syscolpars
实时查询的方式(需要DAC),
我的建议是上面的条件,可能再加上ofELSE
子句。
再说一次,我不能保证任何这种解释都是正确的,但它确实有效:)
如果有人有更准确的DBCC CHECKIDENT
后台操作信息,
我很想学习,请分享!
祝你好运,
??????