web-dev-qa-db-it.com

Reimposta il seme dell'identità dopo aver eliminato i record in SQL Server

Ho inserito i record in una tabella di database di SQL Server. Nella tabella è stata definita una chiave primaria e il seme di identità con incremento automatico è impostato su "Sì". Questo viene fatto principalmente perché in SQL Azure, ogni tabella deve avere una chiave primaria e un'identità definita. 

Ma dal momento che devo cancellare alcuni record dalla tabella, il seed di identità per quelle tabelle verrà disturbato e la colonna indice (che viene generata automaticamente con un incremento di 1) verrà disturbata.

Come posso ripristinare la colonna Identity dopo aver eliminato i record in modo che la colonna abbia una sequenza in ordine numerico crescente?

La colonna Identity non viene utilizzata come chiave esterna in nessun punto del database.

553
Romil N

Il comando DBCC CHECKIDENT management viene utilizzato per ripristinare il contatore dell'identità. La sintassi del comando è:

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

Esempio: 

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

Non era supportato in versioni precedenti del database SQL di Azure, ma ora è supportato.


Si noti che l'argomento new_reseed_value è variato tra le versioni di SQL Server in base alla documentazione

Se le righe sono presenti nella tabella, la riga successiva viene inserita con il valore new_reseed_value. Nella versione di SQL Server 2008 R2 e precedenti, la riga successiva inserita utilizza new_reseed_value + il valore di incremento corrente.

Tuttavia, Trovo che queste informazioni siano fuorvianti (in realtà è semplicemente sbagliato) perché il comportamento osservato indica che almeno SQL Server 2012 utilizza ancora new_reseed_value + la logica del valore di incremento corrente. Microsoft contraddice persino il proprio Example C trovato nella stessa pagina:

C. Forzare il valore di identità corrente a un nuovo valore

L'esempio seguente impone il valore di identità corrente nel AddressTypeID colonna nella tabella AddressType su un valore di 10 . Poiché la tabella ha righe esistenti, la riga successiva inserita utilizzerà 11 come valore, ovvero il nuovo valore di incremento corrente definito per valore della colonna più 1.

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

Tuttavia, tutto questo lascia un'opzione per un comportamento diverso sulle versioni di SQL Server più recenti. Immagino che l'unico modo per essere sicuri, fino a quando Microsoft chiarirà le cose nella sua documentazione, è di fare test reali prima dell'uso.

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

Dove 0 è identity Valore iniziale

188
anil shah

Va notato che IF all dei dati viene rimosso dalla tabella tramite DELETE (cioè nessuna WHERE clausola), quindi finché a) le autorizzazioni lo consentono, e b) non ci sono FK che fanno riferimento al tabella (che sembra essere il caso qui), utilizzando TRUNCATE TABLE sarebbe preferibile in quanto fa un DELETEe più efficiente reimposta il seme IDENTITY allo stesso tempo. I seguenti dettagli sono presi dalla pagina MSDN per TRUNCATE TABLE :

Rispetto all'istruzione DELETE, TRUNCATE TABLE presenta i seguenti vantaggi:

  • Viene utilizzato meno spazio per la registrazione delle transazioni.

    L'istruzione DELETE rimuove le righe una alla volta e registra una voce nel log delle transazioni per ogni riga eliminata. TRUNCATE TABLE rimuove i dati deallocando le pagine di dati utilizzate per memorizzare i dati della tabella e registra solo le deallocazioni della pagina nel log delle transazioni.

  • In genere vengono utilizzati meno blocchi.

    Quando l'istruzione DELETE viene eseguita utilizzando un blocco di riga, ogni riga della tabella è bloccata per l'eliminazione. TRUNCATE TABLE blocca sempre la tabella (incluso un blocco schema (SCH-M)) e la pagina ma non ogni riga.

  • Senza eccezioni, nella tabella vengono lasciate zero pagine.

    Dopo l'esecuzione di una istruzione DELETE, la tabella può ancora contenere pagine vuote. Ad esempio, le pagine vuote in un heap non possono essere deallocate senza almeno un blocco di tabella esclusivo (LCK_M_X). Se l'operazione di cancellazione non utilizza un blocco di tabella, la tabella (heap) conterrà molte pagine vuote. Per gli indici, l'operazione di cancellazione può lasciare pagine vuote dietro, anche se queste pagine saranno deallocate rapidamente da un processo di pulizia in background.

Se la tabella contiene una colonna Identity, il contatore per quella colonna viene reimpostato sul valore di inizializzazione definito per la colonna. Se non è stato definito alcun seme, viene utilizzato il valore predefinito 1. Per conservare il contatore dell'identità, utilizzare invece DELETE.

Quindi il seguente:

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

Diventa solo:

TRUNCATE TABLE [MyTable];

Si prega di consultare la documentazione TRUNCATE TABLE (collegata sopra) per ulteriori informazioni sulle restrizioni, ecc.

71
Solomon Rutzky

Sebbene la maggior parte delle risposte suggerisca RESEED a 0, ma molte volte abbiamo bisogno di eseguire nuovamente il resement al prossimo ID disponibile

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)

Questo controllerà la tabella e ripristinerà il prossimo ID.

62
Atal Kishore

Ho provato @anil shahs answer e ha resettato l'identità. Ma quando è stata inserita una nuova riga, ha ottenuto identity = 2. Così, invece, ho modificato la sintassi in:

DELETE FROM [TestTable]

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

Quindi la prima riga otterrà l'identità = 1.

57
Mikael Engver

Sebbene la maggior parte delle risposte suggerisca RESEED a 0, e mentre alcuni lo vedono come un difetto per le tabelle TRUNCATED, Microsoft ha una soluzione che esclude ID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Questo controllerà la tabella e resetterà il successivo ID. Questo è disponibile da MS SQL 2005 a oggi.

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

15
SollyM

@Giacobbe

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

Ho lavorato per me, ho solo dovuto cancellare tutte le voci prima dalla tabella, poi ho aggiunto il precedente in un punto trigger dopo l'eliminazione. Ora ogni volta che cancello una voce viene presa da lì.

5
epic

Reimposta colonna identità con nuovo ID ...

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

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

emettere 2 comandi può fare il trucco

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

il primo resetta l'identità a zero e il successivo lo imposterà al successivo valore disponibile - Jacob

4
jacob

Questa è una domanda comune e la risposta è sempre la stessa: non farlo. I valori di identità dovrebbero essere trattati come arbitrari e, come tali, non esiste un ordine "corretto".

4
Ben Thul

La tabella Truncate è preferibile perché cancella i record, reimposta il contatore e recupera lo spazio. 

Delete e CheckIdent dovrebbero essere usati solo dove le chiavi esterne ti impediscono di troncare

3
Dyna Dave

Esegui questo script per reimpostare la colonna Identity. Dovrai effettuare due modifiche. Sostituisci tabellaXYZ con qualsiasi tabella che devi aggiornare. Inoltre, il nome della colonna Identity deve essere eliminato dalla tabella temporanea. Questo era istantaneo su un tavolo con 35.000 righe e 3 colonne. Ovviamente, esegui il backup della tabella e prima prova questo in un ambiente di 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

Usa questa stored procedure:

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'

Sto solo rivisitando la mia risposta. Mi sono imbattuto in un comportamento strano in SQL Server 2008 R2 di cui dovresti essere a conoscenza.

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 prima selezione produce 0, Item 1.

Il secondo produce 1, Item 1. Se esegui il reset subito dopo aver creato la tabella, il valore successivo è 0. Onestamente, non mi sorprende che Microsoft non riesca a ottenere questa roba giusta. L'ho scoperto perché ho un file di script che popola le tabelle di riferimento che a volte eseguo dopo che ho ricreato le tabelle e talvolta quando le tabelle sono già state create.

1
costa

Per una completa DELETE righe e ripristinare il conteggio IDENTITÀ, io uso questo (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

Io uso il seguente script per fare questo. C'è solo uno scenario in cui produrrà un "errore", che è se hai cancellato tutte le righe dalla tabella, e IDENT_CURRENT è attualmente impostato su 1, cioè c'era solo una riga nella tabella per cominciare.

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)

Ciò imposterà il valore di identità corrente su 0.

Inserendo il valore successivo, il valore dell'identità viene incrementato a 1.

0
Bimzee