SQL Server标识列的行为不正常?

SQL Server标识列的行为不正常?,sql,sql-server,Sql,Sql Server,我用代码创建了一个名为tblEmployees的表- Create table tblEmployees ( EmployeeID int identity primary key, Name nvarchar(30), Salary float, Gender tinyint ) 然后我插入值- insert into tblEmployees values ('Akmal', 5000, 0) insert into tblEmployees values

我用代码创建了一个名为tblEmployees的表-

Create table tblEmployees
(
    EmployeeID int identity primary key,
    Name nvarchar(30),
    Salary float,
    Gender tinyint
)
然后我插入值-

insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
但是,当显示值时-

EmployeeID  Name      Salary    Gender
2           Akmal     5000          0
3           Shakira   6000          1
4           Kiron     7000          2
5           Jamil     5500          0
7           Faul      4800          4
我的问题是,为什么EmployeeID列以2开头?6号在哪里?该值是否应自动递增?

不要依赖标识列来生成一组连续的无间隙值。时期这根本不能保证;有几件事可能会造成间隙,例如回滚、删除、重新设定种子等。我不相信您用上面的代码重现了这个问题;在这些INSERT语句之间可能还有其他活动

对于这样的替代值和无意义的值,您真的不应该关心是否存在间隙。如果您关心差距,请使用不同的技术,例如可序列化的max+1解决方案-请注意,您需要在可伸缩性/并发性方面权衡差距。你接受的另一个答案,但我怀疑会被删除,它说:

如果希望标识列具有可靠且指定的值,则需要将该列上的identity_INSERT设置为ON,插入具有特定ID值的值,然后将identity_INSERT设置为OFF

仅当您已经知道要插入到该列中的值时,此操作才有效。这首先违背了身份财产的目的。如果您还不知道要插入哪些值,例如下一个ID是什么,则意味着您需要从表中选择MAX,并向其中添加1。这意味着整个过程都需要序列化,否则其他人可以读取相同的最大值并向其中添加相同的+1。因此,如果总是要覆盖生成的值,那么除了使IDENTITY属性无用之外,它还通过有效地将并发性限制为1而扼杀了可伸缩性。我强烈建议您在实施该方法之前对其进行权衡


我建议你改为使用身份栏,不要挂在空白处。他们会发生的,你对此无能为力,也不应该真的担心。谁在乎是否没有员工6?

您的T-SQL脚本不完整,因为生成的“我的身份”值以1开头,以5结尾

注0:我只是想描述一下丢失标识值的一些原因

注意1:不要在生产服务器上运行此脚本

注2:在列定义中使用标识表示标识1,1标识设定值/初始值=1,增量值=1

注意3:如果您不知道这个问题,那么应该避免使用DBCC CHECKIDENT

第一个缺失值可能是一种解释:

为什么EmployeeID列以2开头

运行以下脚本:

IF OBJECT_ID(N'dbo.tblEmployees') IS NOT NULL
    DROP TABLE dbo.tblEmployees;
GO
Create table tblEmployees
(
    EmployeeID int identity primary key,
    Name nvarchar(30),
    Salary float,
    Gender tinyint
)
GO
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY  #1];
/*
Last IDENTITY #1
----------------
5
*/
GO
此时,为该表生成的最后一个标识值是5,而不是您的示例中的7

SELECT * FROM dbo.tblEmployees;
/*
EmployeeID  Name                           Salary                 G
----------- ------------------------------ ---------------------- -
1           Akmal                          5000                   0
2           Shakira                        6000                   1
3           Kiron                          7000                   2
4           Jamil                          5500                   0
5           Faul                           4800                   4
*/
GO
所有行都有连续的标识值:没有间隙

现在,由于某些原因,有人删除了dbo.tblEmployees中的所有行,并决定重新设置最后一个标识值的种子,即从5到1的5到1

DELETE dbo.tblEmployees;
GO
DBCC CHECKIDENT('dbo.tblEmployees', RESEED, 1);
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY  #2];
/*
Last IDENTITY #2
----------------
1
*/
GO
现在,最后一个标识值是1,因为重新设定了种子1

insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT * FROM dbo.tblEmployees;
GO
/*
EmployeeID  Name                           Salary                 Gender
----------- ------------------------------ ---------------------- ------
2           Akmal                          5000                   0
3           Shakira                        6000                   1
4           Kiron                          7000                   2
5           Jamil                          5500                   0
6           Faul                           4800                   4
*/
当我再次插入这些行时,第一次生成的标识值是2

为什么??原因如下所述:

如果自创建表以来未向表中插入任何行,或者使用 TRUNCATE TABLE语句,运行DBCC CHECKIDENT后插入的第一行使用新的_reseed_值作为标识。 否则,插入的下一行将使用新的_重设种子_值+当前增量值

最后一个公式解释了为什么这次第一个标识值为2:

新的种子重新设定值为1-由于种子重新设定1+当前增量值1-请参见注释2=1+1=2

注4:如果使用TRUNCATE TABLE而不是DELETE,则TRUNCATE TABLE之后插入的第一行的ID=seed值请参见注2或new_reseed_值=1。所以,在这种情况下,您不需要DBCC…,重新设定种子,1

第二个缺失值可能是一种解释:

6号在哪里


我不知道为什么我的上一个答案被否决、删除或阻止,但如果你想拥有一个具有可靠和指定值的标识列,那么你需要在该列上将identity_INSERT设置为ON,插入具有特定ID值的值,然后将identity_INSERT设置为OFF


如果您不关心这些值最终是什么(这可能是Employees表的情况),那么请忘记它,并学会接受这些值最终是什么。

您能解释一下原因吗?现在没什么有趣的,因为我的共享托管SQL Server上有一个跳过1000 id的数据库。至少我知道有人看到了它的古怪之处。@Michael,那个有点不一样;看见这与SQL Server 2012中序列的引入如何改变标识值的缓存机制有关。@AaronBertrand
好吧,至少我现在可以解释了!非常感谢你的朋友。@user2989408哈?如果是外键,则在子项存在时不能删除父项。但无论如何,关于重用任何值,我到底说了些什么?IDENTITY1,1将以1的增量从1开始标识列。问题在于假设EmployeeID是否有间隙很重要。请参阅我的答案,了解为什么这个修改identity_INSERT的建议毫无意义。现实情况:我必须导入数据进行查找上一个实现中的表。为了向后兼容,我必须尊重上一个实现中的ID,之后所有新的查找值都将获得自动生成的值。在本例中,解决方案是编写部署后脚本,将IDENTITY_INSERT设置为OFF,然后设置为ON。对不起,和你不同,我不是在说理论,我是在说现实生活中的情况。你写道:同样,要做到这一点,你需要知道插入什么值。这意味着您需要从表中选择MAX。这整件事都需要序列化,否则其他人可以读取你的最大值并向其中添加相同的+1,伙计,读我写的:用特定的ID值插入你的值。你在编码之前读过需求吗?杜德,如果他知道ID值,为什么他会有一个标识列?因为有时候你需要使用一组严格指定的值,然后将其重置为标识,以便以增量方式填充新的ID,让表有它自己的生命。这就是为什么可以将IDENTITY_INSERT设置为OFF和ON的确切原因。
DELETE dbo.tblEmployees WHERE EmployeeID = 6
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY  #3];
/*
Last IDENTITY #3
----------------
7
*/
GO
SELECT * FROM dbo.tblEmployees;
GO
/*
EmployeeID  Name                           Salary                 Gender
----------- ------------------------------ ---------------------- ------
2           Akmal                          5000                   0
3           Shakira                        6000                   1
4           Kiron                          7000                   2
5           Jamil                          5500                   0
7           Faul                           4800                   4
*/