ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

删除记录后如何重置标识列,以便该列按数字升序排列?

标识列不用作数据库中任何地方的外键。

“在 SQL Azure 中”-“每个表都必须有一个主键”-真-“和身份定义”-假。身份和主键是正交概念。标识列不必是表的 PK。主键不必是标识列。
好的。我的概念可能是错误的。但是现在我已经用 PK 和 Identity Seed 定义了表结构。如果我必须删除一些行,我怎么能以正确的数字升序重置身份种子
我总是会争辩说,如果您关心身份列中生成的实际数值,那么您就是在滥用它们。对于标识列,您应该关心的是它会自动生成唯一值(耶!)并且您可以将这些值存储在数字列中(该位仅与声明列以保存这些值有关)。您不应该将它们展示给任何人,因此它们采用什么价值观并不重要。
您可以像其他提到的那样使用 dbcc 检查标识,但请注意主键对于 sql db v12 不是必需的
@Damien_The_Unbeliever 仅仅因为想要重置 id 列并不意味着他们正在向最终用户展示它。如果不需要,无论如何也不可能做到。

P
Petr Abdulin

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

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

例子:

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

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

多亏了 Solomon Rutzky,命令的 docs 现在已修复。


语法是... DBCC CHECKIDENT ('[TestTable]', RESEED, 0) GO
从即将发布的版本(V12 / Sterling)开始,似乎支持 DBCC CHECKIDENTazure.microsoft.com/en-us/documentation/articles/… 不过,对于这种特殊情况,我仍然推荐 TRUNCATE TABLE :)
在“GO”出现在另一行之前,它对我不起作用。
非常适合我。值得指出的是,在重新播种表时,如果您想重新播种以便您的第一条记录为 ID 1,那么重新播种命令必须重新播种为 0,以便下一条记录为 ID 1。
@DavidA.Gray、Petr 和其他人:是的,文档既具有误导性(由于缺少关键场景)又不正确(由于实际上没有版本之间的行为有任何变化)。我写了一篇关于文档内容的帖子,通过一些测试显示了实际行为,并更新了实际文档(现在我们可以,因为它在 GitHub 上):How Does DBCC CHECKIDENT Really Work When Resetting the Identity Seed (RESEED)?。我还修正了示例 C 中的一个错字。
S
Serj Sagan
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

其中 0 是 identity 起始值


如果表是空的,比如你刚刚调用了 TRUNCATE,那么新的种子值应该是下次使用的值(即 1 而不是 0)。如果表不为空,它将使用 new_reseed_value + 1MSDN
@kjbartel、Anil 和其他人:这不像“如果桌子是空的”那么简单。文档缺少表因 DELETE 而不是 TRUNCATE 而为空的情况,在这种情况下它也是 new_reseed+value + 1。我写了一篇关于这个的帖子,通过一些测试展示了实际行为,并更新了实际的文档(现在我们可以,因为它在 GitHub 上):How Does DBCC CHECKIDENT Really Work When Resetting the Identity Seed (RESEED)?
M
Mike G

尽管大多数答案都建议将 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。


这是唯一一个 100% 有效的答案
稍微短一点:declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
正如 doc 中所报告的,仅使用 CHECKIDENT 可以实现相同的结果:执行 DBCC CHECKIDENT (table_name, RESEED,new_reseed_value) 并将 new_reseed_value 设置为非常低的值,然后运行 DBCC CHECKIDENT (table_name, RESEED) 以更正值。
S
Solomon Rutzky

应该注意的是,如果通过 DELETE(即没有 WHERE 子句)从表中删除 所有 数据,那么只要 a) 权限允许,并且 b ) 没有引用该表的 FK(此处似乎就是这种情况),使用 TRUNCATE TABLE 将是首选,因为它执行更有效的 DELETE 并且 重置 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 文档(上面链接)。


虽然在正确的情况下效率更高,但这并不总是一种选择。 Truncate 不会在针对其定义了 FK 的表上执行。即使没有依赖记录,如果存在约束,截断也会失败。此外,truncate 需要 ALTER 权限,而 Delete 只需要 DELETE。
@Rozwel 是的,但我已经对我的回答进行了限定,说明需要适当的权限。此外,该问题特别指出没有 FK。但是,为了清楚起见,我更新以指定“无 FK”限制。感谢您指出了这一点。
唯一的问题是任何 FK 都会阻止截断。可以(尽管不寻常)针对不属于 PK 或标识列的唯一约束进行 FK。
@Rozwel 再次正确,但从问题中假设没有唯一约束似乎是合理的,因为 PK 仅由于 OP 的理解(正确与否)而存在,即 Azure SQL 数据库需要它。无论如何,我都是为了减少歧义,所以我再次更新了。谢谢。
在表上拥有外键并不罕见,并且任何外键的存在都会禁止 TRUNCATE TABLE。今天早些时候,当我尝试在一个表上运行 TRUNCATE TABLE 时,我刚刚发现了这一点,该表具有一个外键,该外键针对表中的其他两个列和外表中的唯一索引强制执行。
S
Samuel Rondeau-Millaire

我尝试了 @anil shahs 答案,它重置了身份。但是当插入一个新行时,它得到了 identity = 2。因此,我将语法更改为:

DELETE FROM [TestTable]

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

然后第一行将获得标识 = 1。


R
RealSollyM

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

DBCC CHECKIDENT ('[TestTable]', RESEED)

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

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


不幸的是,这不是真的。刚刚检查了 MS SQL 2014 服务器。
实际上,对于 SQL 2014 来说确实如此。我刚刚对其进行了测试,它对我有用。
这对我在 SQL 2012 上的工作方式不一致。有时它使用下一个可用的,正如我所期望的那样,有时它似乎卡在表中的旧值上。指定种子总是有效的。
在 SQL 2016 上对我不起作用 - 它只是将身份种子保持原样。它可能曾经对我正常工作,但也可能是我的手指问题。无法让它再次工作
该消息指示成功,Checking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.,但在新插入时它仍在使用不正确的种子。
R
Rich Benner

发出 2 命令可以解决问题

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

第一个将标识重置为零,然后将其设置为下一个可用值--jacob


DBCC CHECKIDENT ('[TestTable]', RESEED) 没有重新播种到下一个可用值
这是“重新设置身份列”选项打开时 RedGate Data Compare 使用的方法。我已经对它进行了广泛的测试(我的意思是在 SQL 代码中,而不是在 RedGate 工具中),并且它工作可靠。 (除了偶尔使用 RedGate 试用版外,我与 RedGate 没有任何关系)
M
Mario Varchmin

我刚刚成功使用了DBCC CHECKIDENT

注意事项:

不接受引用表名方括号时

DBCC CHECKIDENT('TableName',RESEED,n) 将重置回 n+1 例如 DBCC CHECKIDENT('tablename',RESEED,27) 将从 28 开始

例如 DBCC CHECKIDENT('tablename',RESEED,27) 将从 28 开始

如果您在未设置新的起始 ID 时遇到问题 - 请注意这一点,您可以通过以下方式解决此问题:

    DECLARE @NewId as INT  
    SET @NewId =  (SELECT MAX('TableName')-1  AS ID FROM TableName)
    DBCC CHECKIDENT('TableName',RESEED,@MaxId)


e
epic

@雅各布

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

为我工作,我只需要先从表中清除所有条目,然后在删除后将上述内容添加到触发点中。现在,每当我删除一个条目时,都会从那里获取。


DBCC CHECKIDENT 仅在删除后才起作用。您不妨使用截断。但是,如果您需要其余数据,请不要使用它。此外 truncate 不会给出已删除记录的记录计数。
d
d219

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

DeleteCheckIdent 应仅在外键阻止您截断时使用。


e
eathan

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

DECLARE @MAX INT
SELECT @MAX=ISNULL(MAX(Id),0) FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)

C
Chris Mack

我使用以下脚本来执行此操作。只有一种情况会产生“错误”,即如果您已从表中删除所有行,并且 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)
;

B
Ben Thul

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


对于生产环境来说确实如此,但在开发过程中,我喜欢记住某些实体具有特定的 ID,这些 ID 是从播种脚本中填充的。它使在开发过程中浏览数据库变得更加容易。
诸如此类的答案完全是理论上的,它们很少符合现实世界的需求。不如不要用你的教条洗脑,而是回答 OP 问题...
有意思,兄弟。我的论点是:如果您想为列指定值,请不要在列上选择使执行变得困难的属性。代码味道是这样的:如果每次将记录插入表中时,您都为标识列指定了一个值,那么您就没有标识列。身份的全部意义在于让服务器为您创造价值。因此,如果您每次都覆盖它,那么您就不会以非零成本获得任何收益。此外,在 ad hominem 论点上做得很好。
我当然同意你的论点。从表面上看,OP 肯定做错了,但也许在帖子中没有说明更深层次的需要,即 OP 认为与回答他的问题无关。因此,回答这个问题,并给出“做和不做”的建议作为答案的一部分。顺便说一句,我从来没有攻击过你的角色...... ad hominem 的意思是我称你为愚蠢之类的......
虽然在大多数情况下确实如此,但在某些情况下重新播种表是合法的。例如,我正在处理一个新建项目,该项目必须从一个确定的点开始,以说明它正在替换的前任中的现有行。在开发过程中重新播种是一个合法的用例,IMO。
M
Matthew Baic

运行此脚本以重置标识列。您将需要进行两项更改。将 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

这并不完全正确: SET IDENTITY_INSERT 位于错误的位置。它不会绕过 TRUNCATE,它会绕过 INSERT INTO(因此是 identity_INSERT)。此外,这仅在需要保留数据时使用,否则与仅运行单个 TRUNCATE 语句相比效率非常低。
K
KimvdLinde

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

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

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


B
Bimzee
DBCC CHECKIDENT (<TableName>, reseed, 0)

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

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


c
costa

使用这个存储过程:

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。老实说,我并不奇怪微软无法正确处理这些内容。我发现它是因为我有一个脚本文件来填充引用表,我有时会在重新创建表后运行,有时会在表已创建时运行。


F
Fandango68

对于完整的 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

A
Ali Sufyan

除非您将整个表清理干净,否则重新设置为 0 不是很实用。

否则,安东尼雷蒙德给出的答案是完美的。首先获取标识列的最大值,然后使用最大值对其进行播种。


A
Abdul Hannan Ijaz

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

如果我们需要删除并需要重置种子,请始终记住,如果从未填充过表并且您使用了 DBCC CHECKIDENT('tablenem',RESEED,0),那么第一条记录将获得身份 = 0,如 msdn documentation 中所述

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


听起来我的想法是只删除一些记录。
这完全是错误的——使用截断并不总是更好,事实上,只有在一些非常有限和特定的场景中才会更好。天堂禁止有人听从你的建议,然后需要回滚。
@Thronk 你为什么暗示 TRUNCATE 会阻止 ROLLBACK 按预期行事? ROLLBACK 仍然回滚。即使 DB 设置为 BULK_LOGGED
TRUNCATE 是 DDL 操作,它没有记录在日志文件中。除非它是交易的一部分(问题或本答案中未提及)。每当有人说某事总是正确的,这是一个相当安全的赌注,他们是错的。
这是唯一指出 RESEED 行为不同的答案,具体取决于先前是否使用过该序列。在多个空表中重新设置相同值(其中一些表之前已填充),将导致插入到每个表中的第一条记录的初始值不同。
s
slfan

第一:身份规范只是:“否”>>保存数据库执行项目

之后:身份规范只是:“是”>>保存数据库执行项目

你的数据库ID,PK从1开始>>