删除SQL Server中的记录后重置标识种子

时间:2014-02-17 08:51:49

标签: sql sql-server database sql-server-2008 azure-sql-database

我已将记录插入SQL Server数据库表。该表定义了主键,并且自动增量标识种子设置为“是”。这主要是因为在SQL Azure中,每个表都必须定义主键和标识。

但是由于我必须从表中删除一些记录,这些表的标识种子将受到干扰,而索引列(自动生成的增量为1)将受到干扰。

删除记录后如何重置标识列,以便列按数字顺序递增?

标识列不会在数据库中的任何位置用作外键。

20 个答案:

答案 0 :(得分:958)

DBCC CHECKIDENT管理命令用于重置身份计数器。命令语法为:

DBCC CHECKIDENT (table_name [, { NORESEED | { RESEED [, new_reseed_value ]}}])
[ WITH NO_INFOMSGS ]

示例:

DBCC CHECKIDENT ('[TestTable]', RESEED, 0);
GO

以前版本的Azure SQL数据库不支持它,但现在支持。


请注意,new_reseed_value参数因SQL Server版本according to documentation而异:

  

如果表中存在行,则使用 new_reseed_value 值插入下一行。在SQL Server 2008 R2及更早版本中,插入的下一行使用 new_reseed_value +当前增量值。

然而,我发现此信息具有误导性(实际上只是错误),因为观察到的行为表明至少SQL Server 2012仍然使用 new_reseed_value +当前增量值逻辑。微软甚至与在同一页面上找到的自己的Example C相矛盾:

  

℃。将当前标识值强制为新值

     

以下示例强制执行当前标识值   AddressType表中的AddressTypeID列的值为10。   因为表有现有行,所以插入的下一行将使用11   作为值,即为其定义的新当前增量值   列值加1。

USE AdventureWorks2012;  
GO  
DBCC CHECKIDENT ('Person.AddressType', RESEED, 10);  
GO

但是,这一切都为新的SQL Server版本留下了不同行为的选项。我想在确保微软在自己的文档中清理内容之前,唯一可以确定的是在使用前进行实际测试。

答案 1 :(得分:194)

DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

其中0是identity起始值

答案 2 :(得分:74)

应该注意的是,IF 所有数据都是通过DELETE从表中删除的(即没有WHERE子句),那么只要a)权限允许它,以及b)没有引用该表的FK(这里似乎是这种情况),使用TRUNCATE TABLE将是首选,因为它更有效DELETE 和< / em>同时重置IDENTITY种子。以下详细信息来自TRUNCATE TABLE的MSDN页面:

  

与DELETE语句相比,TRUNCATE TABLE具有以下优点:

     
      
  • 使用较少的事务日志空间。

         

    DELETE语句一次删除一行,并在事务日志中为每个已删除的行记录一个条目。 TRUNCATE TABLE通过释放用于存储表数据的数据页来删除数据,并仅在事务日志中记录页面解除分配。

  •   
  • 通常使用较少的锁。

         

    使用行锁执行DELETE语句时,表中的每一行都被锁定以进行删除。 TRUNCATE TABLE总是锁定表(包括模式(SCH-M)锁)和页面而不是每一行。

  •   
  • 无一例外,表格中都留有零页。

         

    执行DELETE语句后,表仍可包含空页。例如,如果没有至少一个独占(LCK_M_X)表锁,则无法释放堆中的空页。如果删除操作不使用表锁,则表(堆)将包含许多空页。对于索引,删除操作可以留下空白页面,尽管这些页面将通过后台清理过程快速释放。

  •   
     

如果表包含标识列,则该列的计数器将重置为为该列定义的种子值。如果未定义种子,则使用默认值1。要保留身份计数器,请改用DELETE。

以下内容:

DELETE FROM [MyTable];
DBCC CHECKIDENT ('[MyTable]', RESEED, 0);

变得公正:

TRUNCATE TABLE [MyTable];

请参阅TRUNCATE TABLE文档(上面链接)以获取有关限制等的其他信息。

答案 3 :(得分:66)

虽然大多数答案建议重新设定为0,但很多时候我们需要重新调整到下一个可用的

declare @max int
select @max=max([Id])from [TestTable]
if @max IS NULL   //check when max is returned as null
  SET @max = 0
DBCC CHECKIDENT ('[TestTable]', RESEED,@max)

这将检查表并重置为下一个ID。

答案 4 :(得分:58)

我尝试了@anil shahs回答并重置了身份。但是当插入新行时,它获得了identity = 2。所以我把语法改为:

DELETE FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED, 0)
GO

然后第一行将获得identity = 1。

答案 5 :(得分:15)

虽然大多数答案都建议RESEED0,虽然有些人认为这是TRUNCATED表的缺陷,但微软有一个排除ID的解决方案

DBCC CHECKIDENT ('[TestTable]', RESEED)

这将检查表并重置为下一个ID。从MS SQL 2005到现在,这已经可用了。

https://msdn.microsoft.com/en-us/library/ms176057.aspx

答案 6 :(得分:5)

发出2命令可以做到这一点

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

第一个将身份重置为零,然后下一个将其设置为下一个可用值   - 雅各布

答案 7 :(得分:5)

@jacob

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

为我工作,我只需要先从表中清除所有条目,然后在删除后将其添加到触发点。现在每当我删除一个条目就从那里开始。

答案 8 :(得分:5)

这是一个常见的问题,答案总是一样的:不要这样做。身份值应被视为任意,因此,没有“正确”的顺序。

答案 9 :(得分:4)

使用新ID重置标识列...

{{1}}

答案 10 :(得分:4)

Truncate表是首选,因为它清除记录,重置计数器并回收磁盘空间。

仅当外键阻止您截断时,才应使用

DeleteCheckIdent

答案 11 :(得分:2)

运行此脚本以重置标识列。您需要进行两项更改。将tableXYZ替换为您需要更新的任何表。此外,需要从临时表中删除标识列的名称。这是在一张35,000行和一张桌子上的瞬间。 3列。显然,备份表并首先在测试环境中尝试此操作。


select * 
into #temp
From tableXYZ

set identity_insert tableXYZ ON

truncate table tableXYZ

alter table #temp drop column (nameOfIdentityColumn)

set identity_insert tableXYZ OFF

insert into tableXYZ
select * from #temp

答案 12 :(得分:2)

我一直在尝试在开发过程中为大量表完成此操作,这很吸引人。

DBCC CHECKIDENT('www.newsType', RESEED, 1);
DBCC CHECKIDENT('www.newsType', RESEED);

因此,您首先将其设置为1,然后将其设置为表中存在的行的最高索引。快速简便的idex休息。

答案 13 :(得分:1)

使用此存储过程:

IF (object_id('[dbo].[pResetIdentityField]') IS NULL)
  BEGIN
    EXEC('CREATE PROCEDURE [dbo].[pResetIdentityField] AS SELECT 1 FROM DUMMY');
  END
GO

SET  ANSI_NULLS ON
GO
SET  QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[pResetIdentityField]
  @pSchemaName NVARCHAR(1000)
, @pTableName NVARCHAR(1000) AS
DECLARE @max   INT;
DECLARE @fullTableName   NVARCHAR(2000) = @pSchemaName + '.' + @pTableName;

DECLARE @identityColumn   NVARCHAR(1000);

SELECT @identityColumn = c.[name]
FROM sys.tables t
     INNER JOIN sys.schemas s ON t.[schema_id] = s.[schema_id]
     INNER JOIN sys.columns c ON c.[object_id] = t.[object_id]
WHERE     c.is_identity = 1
      AND t.name = @pTableName
      AND s.[name] = @pSchemaName

IF @identityColumn IS NULL
  BEGIN
    RAISERROR(
      'One of the following is true: 1. the table you specified doesn''t have an identity field, 2. you specified an invalid schema, 3. you specified an invalid table'
    , 16
    , 1);
    RETURN;
  END;

DECLARE @sqlString   NVARCHAR(MAX) = N'SELECT @maxOut = max(' + @identityColumn + ') FROM ' + @fullTableName;

EXECUTE sp_executesql @stmt = @sqlString, @params = N'@maxOut int OUTPUT', @maxOut = @max OUTPUT

IF @max IS NULL
  SET @max = 0

print(@max)

DBCC CHECKIDENT (@fullTableName, RESEED, @max)
go

--exec pResetIdentityField 'dbo', 'Table'

重温我的回答。我在sql server 2008 r2中遇到了一个你应该知道的奇怪行为。

drop table test01

create table test01 (Id int identity(1,1), descr nvarchar(10))

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

delete from test01

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

第一个选择产生0, Item 1

第二个产生1, Item 1。如果在创建表后立即执行重置,则下一个值为0.老实说,我并不感到惊讶,微软无法正确使用这些东西。我发现它是因为我有一个脚本文件填充了我在重新创建表时有时会运行的引用表,有时候表已经创建了。

答案 14 :(得分:0)

DBCC CHECKIDENT (<TableName>, reseed, 0)

这会将当前标识值设置为0。

在插入下一个值时,标识值会增加到1。

答案 15 :(得分:0)

对于完整的DELETE行并重置IDENTITY计数,我使用它(SQL Server 2008 R2)

USE mydb

-- ##################################################################################################################
-- DANGEROUS!!!! USE WITH CARE
-- ##################################################################################################################

DECLARE
  db_cursor CURSOR FOR
    SELECT TABLE_NAME
      FROM INFORMATION_SCHEMA.TABLES
     WHERE TABLE_TYPE = 'BASE TABLE'
       AND TABLE_CATALOG = 'mydb'

DECLARE @tblname VARCHAR(50)
SET @tblname = ''

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @tblname

WHILE @@FETCH_STATUS = 0
BEGIN
  IF CHARINDEX('mycommonwordforalltablesIwanttodothisto', @tblname) > 0
    BEGIN
      EXEC('DELETE FROM ' + @tblname)
      DBCC CHECKIDENT (@tblname, RESEED, 0)
    END

  FETCH NEXT FROM db_cursor INTO @tblname
END

CLOSE db_cursor
DEALLOCATE db_cursor
GO

答案 16 :(得分:0)

我使用以下脚本执行此操作。只有一种情况会产生“错误”,即如果您已删除表中的所有行,并且IDENT_CURRENT当前设置为1,即表中只有一行开始

DECLARE @maxID int = (SELECT MAX(ID) FROM dbo.Tbl)
;

IF @maxID IS NULL
    IF (SELECT IDENT_CURRENT('dbo.Tbl')) > 1
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 0)
    ELSE
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 1)
    ;
ELSE
    DBCC CHECKIDENT ('dbo.Tbl', RESEED, @maxID)
;

答案 17 :(得分:0)

重新设置为0不太实际,除非您要清理整个表。

另外,安东尼·雷蒙德(Anthony Raymond)给出的答案是完美的。首先获取最大标识列,然后使用最大种子列。

答案 18 :(得分:-2)

最好尽可能使用 TRUNCATE ,而不是删除所有记录,因为它也不会使用日志空间。

如果我们需要删除并需要重置种子,请始终记住,如果从未填充过表并且您使用了DBCC CHECKIDENT('tablenem',RESEED,0) 然后第一个记录将获得identity = 0 正如msdn documentation

所述
  

在您的情况下,只有重建索引并且不用担心丢失   一系列身份,因为这是一种常见的情况。

答案 19 :(得分:-4)

第一:身份规范只是:&#34;否&#34; &GT;&GT;保存数据库执行项目

之后:身份规范只是:&#34;是&#34; &GT;&GT;保存数据库执行项目

您的数据库ID,PK从1开始&gt;&gt;