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.
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.
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO
Dove 0 è identity
Valore iniziale
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 DELETE
e 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.
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.
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.
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.
@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ì.
Reimposta colonna identità con nuovo ID ...
DECLARE @MAX INT
SELECT @MAX=ISNULL(MAX(Id),0) FROM [TestTable]
DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)
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
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".
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
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
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.
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
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)
;
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.