我已将记录插入到SQL Server数据库表中。该表已定义了主键,并且自动增量标识种子设置为“是”。这样做主要是因为在SQL Azure中,每个表都必须定义一个主键和标识。
但是由于我必须从表中删除一些记录,所以这些表的标识种子将受到干扰,索引列(索引列(以1为增量自动生成)也将受到干扰)。
删除记录后如何重置标识列,以便该列按升序排列?
标识列不用作数据库中任何地方的外键。
我已将记录插入到SQL Server数据库表中。该表已定义了主键,并且自动增量标识种子设置为“是”。这样做主要是因为在SQL Azure中,每个表都必须定义一个主键和标识。
但是由于我必须从表中删除一些记录,所以这些表的标识种子将受到干扰,索引列(索引列(以1为增量自动生成)也将受到干扰)。
删除记录后如何重置标识列,以便该列按升序排列?
标识列不用作数据库中任何地方的外键。
Answers:
该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版本之间有所不同:
如果表中存在行,则将下一行插入new_reseed_value值。在版本SQL Server 2008 R2和更早版本中,插入的下一行使用new_reseed_value +当前增量值。
但是,我发现此信息具有误导性(实际上是完全错误的),因为观察到的行为表明至少SQL Server 2012仍在使用new_reseed_value +当前的增量值逻辑。微软甚至与自己Example C
在同一页面上发现的矛盾:
C.将当前身份值强制为新值
下面的示例将AddressType表的AddressTypeID列中的当前标识值强制为10。由于该表具有现有行,因此插入的下一行将使用11作为值,即为该值定义的新的当前增量值。列值加1。
USE AdventureWorks2012;
GO
DBCC CHECKIDENT ('Person.AddressType', RESEED, 10);
GO
尽管如此,这一切都为较新的SQL Server版本留下了不同行为的选择。我猜想,直到Microsoft清除其自己的文档中的内容之前,唯一可以确定的方法就是在使用前进行实际测试。
DBCC CHECKIDENT
在即将发布的版本(V12 / Sterling)中受支持:azure.microsoft.com/zh-cn/documentation/articles/…虽然,对于这种特殊情况,我仍然建议TRUNCATE TABLE :)
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO
其中0是identity
起始值
DELETE
不是TRUNCATE
,在这种情况下也是如此new_reseed+value + 1
。我写了一篇关于此的文章,通过一些测试显示了实际的行为,并更新了实际的文档(现在由于在GitHub上而已,我们可以这样做):重置身份种子(RESEED)时,DBCC CHECKIDENT如何真正起作用?。
应该注意的是,如果所有数据都是通过DELETE
(即no WHERE
子句)从表中删除的,则只要a)许可允许它,并且b)没有引用该表的FK(似乎是(在这种情况下),则TRUNCATE TABLE
首选使用,因为这样做的效率更高,DELETE
并且可以同时重置IDENTITY
种子。以下详细信息来自MSDN页面的TRUNCATE TABLE:
与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
文档(上面链接)以获取有关限制等的更多信息。
尽管大多数答案都建议将RESEED设置为0,但是很多时候我们只需要重新设置下一个可用的ID
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。
declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
我尝试@anil shahs
回答,它重置了身份。但是,当插入新行时,它得到了identity = 2
。因此,我改为将语法更改为:
DELETE FROM [TestTable]
DBCC CHECKIDENT ('[TestTable]', RESEED, 0)
GO
然后,第一行将获得标识= 1。
尽管大多数答案都建议RESEED
使用0
,尽管有些人将其视为TRUNCATED
表的缺陷,但Microsoft的解决方案不包括ID
DBCC CHECKIDENT ('[TestTable]', RESEED)
这将检查表并重置到下一个表ID
。自MS SQL 2005到当前,该功能已可用。
Checking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.
但在新插入时它仍使用不正确的种子。
发出2条命令可以解决问题
DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)
第一个将标识重置为零,下一个将其设置为下一个可用值-jacob
@雅各布
DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)
为我工作,我只需要首先从表中清除所有条目,然后在删除后在触发点中添加以上内容。现在,每当我删除条目时,都会从那里开始。
Truncate
首选table,因为它会清除记录,重置计数器并回收磁盘空间。
Delete
并且CheckIdent
仅应在外键阻止您截断的地方使用。
使用新ID重置身份列...
DECLARE @MAX INT
SELECT @MAX=ISNULL(MAX(Id),0) FROM [TestTable]
DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)
这是一个常见的问题,答案始终是相同的:不要这样做。标识值应视为任意值,因此,没有“正确”顺序。
运行此脚本以重置标识列。您将需要进行两项更改。将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
使用此存储过程:
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。老实说,我并不惊讶Microsoft无法正确处理此问题。我发现它是因为我有一个脚本文件来填充参考表,这些参考表有时在重新创建表之后运行,有时在表已经创建时运行。
我使用以下脚本来执行此操作。只有一种情况会产生“错误”,即如果您已删除表中的所有行,并且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)
;
对于完整的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
尽可能最好使用 TRUNCATE而不是删除所有记录,因为它也不使用日志空间。
万一我们需要删除并需要重置种子,请记住,如果从未填充过表,而您使用过该表,DBCC CHECKIDENT('tablenem',RESEED,0)
则第一条记录将获得身份= 0,如 msdn文档中所述
在您的情况下,仅重建索引,不必担心会丢失一系列标识,因为这是常见的情况。
TRUNCATE
会阻止ROLLBACK
预期的行为?ROLLBACK仍会回滚。即使DB设置为BULK_LOGGED
。
首先:身份规范仅:“否” >>保存数据库执行项目
之后:Identity Specification仅:“ YES” >>保存数据库执行项目
您的数据库ID,PK从1开始>>