web-dev-qa-db-fra.com

Réinitialiser la graine d'identité après la suppression d'enregistrements dans SQL Server

J'ai inséré des enregistrements dans une table de base de données SQL Server. La table avait une clé primaire définie et le germe d'identité d'incrémentation automatique est défini sur «Oui». Cela est principalement dû au fait que dans SQL Azure, chaque table doit avoir une clé primaire et une identité définies. 

Mais comme je dois supprimer certains enregistrements de la table, la graine d’identité de ces tables sera perturbée et la colonne d’index (qui est générée automatiquement avec un incrément de 1) sera perturbée.

Comment puis-je réinitialiser la colonne d'identité après avoir supprimé les enregistrements afin que la colonne ait une séquence dans l'ordre numérique croissant?

La colonne d'identité n'est pas utilisée comme clé étrangère dans la base de données.

553
Romil N

La commande DBCC CHECKIDENT management est utilisée pour réinitialiser le compteur d’identité. La syntaxe de la commande est la suivante:

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

Exemple: 

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

Il n'était pas pris en charge dans les versions précédentes de la base de données Azure SQL, mais est désormais pris en charge.


Veuillez noter que l'argument new_reseed_value varie selon les versions de SQL Server selon la documentation

Si des lignes sont présentes dans la table, la ligne suivante est insérée avec la valeur new_reseed_value. Dans les versions SQL Server 2008 R2 et antérieures, la ligne suivante insérée utilise new_reseed_value + la valeur d'incrément actuelle.

Cependant, je trouve ces informations trompeuses (en fait, elles sont fausses) car le comportement observé indique qu'au moins SQL Server 2012 utilise toujours new_reseed_value + la logique de la valeur d'incrémentation actuelle. Microsoft contredit même avec son propre Example C trouvé sur la même page:

C. Forcer la valeur d'identité actuelle à une nouvelle valeur

L'exemple suivant force la valeur d'identité actuelle dans le fichier Colonne AddressTypeID de la table AddressType avec une valeur de 10 . Comme la table a des lignes existantes, la ligne suivante insérée utilisera 11 en tant que valeur, c'est-à-dire la nouvelle valeur d'incrément actuelle définie pour le valeur de colonne plus 1.

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

Néanmoins, tout cela laisse une option pour un comportement différent sur les versions plus récentes de SQL Server. Je suppose que la seule façon d’en être sûr, jusqu’à ce que Microsoft clarifie les éléments de sa propre documentation, consiste à effectuer des tests avant leur utilisation.

920
Petr Abdulin
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

Où 0 est identity Valeur initiale

188
anil shah

Il convient de noter que SI toutes des données sont en train d'être supprimées de la table via la variable DELETE (c'est-à-dire, pas une clause WHERE), tant que table (ce qui semble être le cas ici), en utilisant TRUNCATE TABLE serait préférable, car une variable plus efficace DELETEet réinitialise la valeur de départ IDENTITY au même moment. Les détails suivants sont extraits de la page MSDN pour TRUNCATE TABLE :

Comparé à l'instruction DELETE, TRUNCATE TABLE présente les avantages suivants:

  • Moins d'espace de journal des transactions est utilisé.

    L'instruction DELETE supprime les lignes une à la fois et enregistre une entrée dans le journal des transactions pour chaque ligne supprimée. TRUNCATE TABLE supprime les données en désallouant les pages de données utilisées pour stocker les données de la table et enregistre uniquement les désallocations de page dans le journal des transactions.

  • Moins de verrous sont généralement utilisés.

    Lorsque l'instruction DELETE est exécutée à l'aide d'un verrou de ligne, chaque ligne de la table est verrouillée pour la suppression. TRUNCATE TABLE verrouille toujours la table (y compris un verrou de schéma (SCH-M)) et la page, mais pas chaque ligne.

  • Sans exception, il ne reste aucune page dans la table.

    Une fois une instruction DELETE exécutée, la table peut toujours contenir des pages vides. Par exemple, les pages vides d'un segment de mémoire ne peuvent pas être désallouées sans au moins un verrou de table exclusif (LCK_M_X). Si l'opération de suppression n'utilise pas de verrou de table, la table (tas) contiendra de nombreuses pages vides. Pour les index, l'opération de suppression peut laisser des pages vides, bien que ces pages soient désallouées rapidement par un processus de nettoyage en arrière-plan.

Si la table contient une colonne d'identité, le compteur de cette colonne est réinitialisé sur la valeur de départ définie pour la colonne. Si aucune graine n'a été définie, la valeur par défaut 1 est utilisée. Pour conserver le compteur d'identité, utilisez plutôt DELETE.

Donc ce qui suit:

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

Devient juste:

TRUNCATE TABLE [MyTable];

Veuillez consulter la documentation TRUNCATE TABLE (lien ci-dessus) pour plus d'informations sur les restrictions, etc.

71
Solomon Rutzky

Bien que la plupart des réponses suggèrent RESEED à 0, nous avons souvent besoin de réensemencer à la prochaine identification disponible.

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)

Cela va vérifier la table et revenir à l'ID suivant.

62
Atal Kishore

J'ai essayé @anil shahs answer et cela a réinitialisé l'identité. Mais quand une nouvelle ligne a été insérée, elle a reçu le identity = 2. J'ai donc changé la syntaxe en:

DELETE FROM [TestTable]

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

Ensuite, la première ligne aura l'identité = 1.

57
Mikael Engver

Bien que la plupart des réponses suggèrent RESEED à 0, et si certaines y voient une faille pour les tables TRUNCATED, Microsoft propose une solution excluant la ID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Cela va vérifier la table et revenir à la prochaine ID. Ceci est disponible depuis MS SQL 2005 to current.

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

15
SollyM

@Jacob

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

Travaillé pour moi, je devais simplement effacer toutes les entrées de la table, puis ajouter ce qui précède dans un point de déclenchement après la suppression. Maintenant, chaque fois que je supprime une entrée est prise à partir de là.

5
epic

Réinitialiser la colonne d'identité avec le nouvel identifiant ...

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

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

l'émission de 2 commandes peut faire l'affaire

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

le premier réinitialise l’identité à zéro et le suivant le définira à la prochaine valeur disponible -- Jacob

4
jacob

C'est une question fréquente et la réponse est toujours la même: ne le faites pas. Les valeurs d'identité doivent être traitées de manière arbitraire et, en tant que telles, il n'y a pas d'ordre "correct".

4
Ben Thul

La table Truncate est préférable car elle efface les enregistrements, réinitialise le compteur et récupère l'espace disque. 

Delete et CheckIdent ne doivent être utilisés que lorsque des clés étrangères vous empêchent de tronquer

3
Dyna Dave

Exécutez ce script pour réinitialiser la colonne d'identité. Vous devrez faire deux changements. Remplacez tableXYZ par la table à mettre à jour. En outre, le nom de la colonne d'identité doit être supprimé de la table temporaire. C'était instantané sur une table avec 35 000 lignes et 3 colonnes. Évidemment, sauvegardez la table et essayez d’abord dans un environnement de test. 


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
2
Matthew Baic

Utilisez cette procédure stockée:

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'

Je reviens sur ma réponse. Je suis tombé sur un comportement étrange dans SQL Server 2008 R2 que vous devriez être au courant.

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

La première sélection produit 0, Item 1.

Le second produit 1, Item 1. Si vous exécutez la réinitialisation juste après la création de la table, la valeur suivante est 0. Honnêtement, je ne suis pas surpris que Microsoft ne puisse pas résoudre ce problème. Je l'ai découvert parce que j'ai un fichier de script qui renseigne les tables de référence que je lance parfois après avoir recréé des tables et parfois lorsque les tables sont déjà créées.

1
costa

Remettre à zéro n'est pas très pratique sauf si vous nettoyez la table dans son ensemble.

sinon, la réponse donnée par Anthony Raymond est parfaite. Obtenez le maximum de la colonne identité en premier, puis ensemencez-le avec max.

0
Ali Sufyan

Pour des lignes complètes DELETE et la réinitialisation du compte IDENTITY, j'utilise ceci (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
0
Fandango68

J'utilise le script suivant pour le faire. Il existe un seul scénario dans lequel une "erreur" est générée, à savoir si vous avez supprimé toutes les lignes de la table et que IDENT_CURRENT est actuellement défini sur 1, c'est-à-dire qu'il n'y avait qu'une seule ligne dans la table pour commencer.

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)
;
0
Chris Mack
DBCC CHECKIDENT (<TableName>, reseed, 0)

Cela définira la valeur d'identité actuelle sur 0.

Lors de l'insertion de la valeur suivante, la valeur d'identité est incrémentée à 1.

0
Bimzee