行为不端的身份

时间:2011-06-16 19:51:36

标签: sql sql-server tsql identity sql-server-2008-r2

执行以下脚本,将表创建并填充到dev数据库中。

SET NOCOUNT ON
Drop Table dbo.Region
GO
CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)
GO

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region One'), 
        ('Region Two');
GO

SELECT * FROM dbo.Region

这可以从一个表现良好的身份字段中得到的结果。

RegionId    RegionName
----------- ------------------
1           Region One
2           Region Two

现在让我们在Identity列中强制使用几个值。

SET NOCOUNT ON
Drop Table dbo.Region
GO
CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)
GO

SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, 'Unknown'),
       (-99, 'N/A');
SET IDENTITY_INSERT dbo.Region OFF;

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region One'), 
        ('Region Two');
GO

SELECT * FROM dbo.Region

输出

RegionId    RegionName
----------- ------------------
-9          Unknown
-99         N/A
2           Region One
3           Region Two

RegionId = 1 去哪了?




编辑在进一步研究中,如果您尝试两次相同的特技,Sql-Server不会 跳过

SET NOCOUNT ON
Drop Table dbo.Region
GO
CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)
GO

SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, 'Unknown'), 
       (-99, 'N/A');
SET IDENTITY_INSERT dbo.Region OFF;

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region One'), 
        ('Region Two');
GO
SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-999, 'Known-Unknown'), 
       (-9999, 'Really N/A');
SET IDENTITY_INSERT dbo.Region OFF;

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region Four'), 
        ('Region Five');
GO

SELECT * FROM dbo.Region

这里的输出是

RegionId    RegionName
----------- ------------------
-9          Unknown
-99         N/A
2           Region One
3           Region Two
-999        Known-Unknown
-9999       Really N/A
4           Region Four
5           Region Five

在之前的案例中,1失踪了。这里4没有丢失!

所以现在这是不可预测的遗失身份!

为什么 RegionId = 1 会丢失,但 RegionId = 4 不会丢失?!

3 个答案:

答案 0 :(得分:6)

IDENTITY(1,1)适用于表格中的第一行

由于您已插入两行,因此种子不再适用

当下一个身份算法检测到表中存在已有记录时,它会向种子开始添加一个,因为1可能已经被使用过。

答案 1 :(得分:3)

我也注意到了这一点,第一个身份值在某种程度上是特殊的。如果在表上执行事务,则会删除第一个标识。对于以下身份,情况并非如此。

原因似乎是current identityNULL值开头:

create table YourTable (id int identity)
dbcc checkident(YourTable)
-->
Checking identity information: current identity value 'NULL', 
current column value 'NULL'.

current identityNULL时,下一个要发出的号码为1。但是任何事务,即使是不使用新身份的事务,都会导致current identityNULL更改为1

set identity_insert YourTable on;
insert YourTable (id) values (-1)
set identity_insert YourTable off;
dbcc checkident(YourTable)
-->
Checking identity information: current identity value '1', 
current column value '1'.

使用current identity 1,下一个号码为2

insert YourTable default values
select * from YourTable
-->
2

因此,新表上的第一个事务对current identity有特殊影响。它不一定是新表,它也发生在truncate table之后。

答案 2 :(得分:1)

来自SQL Server Books Online

IDENTITY [ (seed , increment ) ]
  

种子

     

用于第一行的值是否已加载到表中。

     

增量

     

是否已添加到已加载的上一行的标识值的增量值。

因为您使用IDENTITY_INSERT将第一行加载到表中,所以不需要该实例中的seed。对于IDENTITY_INSERT,还记录了

  

如果插入的值大于当前的标识值   在表中,SQL Server自动使用新插入的值作为   目前的身份价值。

它没有说明在您的问题中描述的情况应该发生什么,但很明显,它只是将当前的身份值设置为MAX(seed,identity_inserted)从下面

CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)

SELECT increment_value,
       last_value as last_value_new_table
FROM sys.identity_columns 
WHERE name ='RegionId' AND object_id=object_id('dbo.Region')

SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, '-9')
SET IDENTITY_INSERT dbo.Region OFF;

SELECT increment_value,
       last_value as last_value_after_insert
FROM sys.identity_columns 
WHERE name ='RegionId' AND object_id=object_id('dbo.Region')

DROP TABLE dbo.Region

返回

increment_value                last_value_new_table
------------------------------ ------------------------------
1                              NULL

increment_value                last_value_after_insert
------------------------------ ------------------------------
1                              1