重新生成丢失的身份值

时间:2018-10-01 08:20:48

标签: sql sql-server auto-increment

如果语句在事务块内失败,是否可以重新创建SQL Server表的标识值?

请输入以下代码:

DECLARE @IdentityTable AS TABLE (ID INT IDENTITY(1, 1), Description VARCHAR(50))

INSERT INTO @IdentityTable (Description) 
VALUES('Test1')

BEGIN TRY 
BEGIN TRANSACTION IdentityTest
    INSERT INTO @IdentityTable (Description) 
    VALUES('Test2')

    INSERT INTO @IdentityTable (Description) 
    VALUES(1/0)

    COMMIT TRANSACTION IdentityTest
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION IdentityTest
END CATCH

INSERT INTO @IdentityTable (Description) 
VALUES('Test4')

SELECT * FROM @IdentityTable

Result

3号身份因ROLLBACK TRANSACTION而丢失。可以恢复吗?

1 个答案:

答案 0 :(得分:5)

您正在尝试使用IDENTITY属性来生成连续的数字并进行维护;这不是IDENTITY的目的。它旨在提供基于当前种子的增量值(仅凭其自身(没有PRIMARY KEY约束或UNIQUE INDEX),甚至不能保证种子可以更改的唯一性(感谢HoneyBadger这么早就提醒我))。

如果INSERT失败,则IDENTITY的值仍将增加。另外,如果要从表中DELETE行,则不会导致每个“后”行的ID都相应地更新。这样您就会有一个缺口。

确保获得递增值的唯一保证方法是在运行时间 使用ROW_NUMBER之类的函数。例如:

SELECT ROW_NUMBER() OVER (ORDER BY ID) AS cID,
       Description
FROM YourTable;

文档的Remarks部分专门声明,不保证连续的值:

  

Identity列可用于生成键值。身份   列上的属性可确保满足以下条件:

     

...

     

交易中的连续值 –插入交易   多行不能保证获得行的连续值   因为其他并发插入可能会在表上发生。如果值   必须是连续的,则交易应使用排他锁   或使用SERIALIZABLE隔离级别。

     

服务器重启或其他故障后的连续值 – SQL Server   出于性能原因可能会缓存身份值,并且某些   数据库故障或服务器期间分配的值可能会丢失   重新开始。这可能导致插入时身份值出现空白。如果   差距是不可接受的,那么应用程序应该使用自己的   生成键值的机制。将序列发生器与   NOCACHE选项可以限制与从未交易的差距   承诺。

     

重复使用值 –对于具有特定标识的给定标识属性   种子/增量,标识值不会被引擎重用。如果一个   特定的插入语句失败或插入语句已滚动   然后,消耗的身份值将丢失,并且不会   再次产生。这可能会导致在后续标识时出现空白   值生成。