SQL Server标识列行为不正常?

时间:2014-01-15 17:50:22

标签: 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 ('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在哪里?这不应该自动增加吗?

3 个答案:

答案 0 :(得分:15)

不要依赖IDENTITY列来生成一组没有间隙的连续值。期。这根本不能保证;有些事情可能导致诸如回滚,删除,重新种植等缺陷。我不相信你用上面的确切代码重现了这个问题;这些INSERT陈述之间可能存在其他活动。

对于这样的代理和无意义的价值观,你真的不应该关心是否存在差距。如果您关心差距,请使用其他技术(例如,可序列化的max()+ 1解决方案) - 请注意您为可伸缩性/并发性问题交换差距。另一个答案(你接受了,但我怀疑会被删除)说:

  

如果要使标识列具有可靠和指定的值,则需要在该列上将IDENTITY_INSERT设置为ON,插入值(使用特定的ID值),然后将IDENTITY_INSERT设置为OFF。

仅当您已经知道要插入该列的值时,此方法才有效。这首先打败了IDENTITY财产的目的。如果您还不知道要插入的值(例如,“下一个”ID是什么),则表示您需要从表中SELECT MAX(),并向其中添加1。这意味着整个事物需要可序列化,否则其他人可以读取相同的MAX()值并向其添加相同的+1。因此,如果你总是要覆盖生成的值,那么除了使IDENTITY属性无用之外,它还会通过有效地将并发性限制为1来杀死可扩展性。我强烈建议你在实现之前强烈权衡这种方法。

我建议你做的是使用IDENTITY列,不要挂在间隙上。它们会发生,你可以做的并不多,而且无论如何它都不应该成为一个问题。谁在乎是否没有员工#6?

答案 1 :(得分:2)

您的T-SQL脚本不完整,因为我的IDENTITY值是从1开始生成的(以5结尾)。

注意#0:我只是试图描述那些缺少IDENTITY值的原因。

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

注意#2:在列定义中使用IDENTITY表示IDENTITY(1,1)< => IDENTITY(seed value/initial value=1,increment value=1)

注意#3:如果您不了解consequences of this command,则应避免使用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

此时为此表生成的最后一个IDENTITY值(如您所见)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

所有行都有连续的IDENTITY值:没有间隙。

现在,由于某些原因,有人从dbo.tblEmployees删除所有行,并决定重置(RESEED)最后一个标识值(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

现在,最后一个IDENTITY值为1(因为RESEED 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
*/

当我再次插入这些行时,第一个生成的IDENTITY值为2(此时)。

为什么?原因在MSDN

中描述

“如果自创建表格后表格中没有插入任何行,或者如果所有行都已删除,则使用 TRUNCATE TABLE语句,运行DBCC后插入的第一行CHECKIDENT使用new_reseed_value作为标识。 否则,插入的下一行使用new_reseed_value +当前增量值。

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

new_reseed_value (是1 - 因为已重置1) +当前增量值(1 - 请参阅注释#2) = 1 + 1 = 2 < / strong>即可。

注意#4:如果您使用的是TRUNCATE TABLE而不是DELETE,则TRUNCATE TABLE之后插入的第一行将具有ID =种子值(请参阅注释#2)或new_reseed_value = 1.因此,在这种情况下,您不需要DBCC(..., RESEED, 1)

第二个缺失值(可能的解释):

  

6在哪里?

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
*/

答案 2 :(得分:-4)

不确定为什么我的上一个答案被删除并删除或阻止,但是如果您想要一个具有可靠和指定值的标识列,那么您需要在该列上将IDENTITY_INSERT设置为ON,插入您的值(具有特定ID) ())然后将IDENTITY_INSERT设置回OFF。

如果你不关心这些值最终会出现什么(这可能就是Employees表的情况),那么就忘记它并学会忍受这些最终的价值。