Restablecer la semilla de identidad después de eliminar registros en SQL Server

682

He insertado registros en una tabla de base de datos de SQL Server. La tabla tenía una clave principal definida y la semilla de identidad de incremento automático se establece en "Sí". Esto se hace principalmente porque en SQL Azure, cada tabla debe tener una clave primaria e identidad definidas.

Pero como tengo que eliminar algunos registros de la tabla, la semilla de identidad para esas tablas se alterará y la columna de índice (que se genera automáticamente con un incremento de 1) se alterará.

¿Cómo puedo restablecer la columna de identidad después de eliminar los registros para que la columna tenga una secuencia en orden numérico ascendente?

La columna de identidad no se utiliza como clave foránea en ninguna parte de la base de datos.

xorpower
fuente
44
"en SQL Azure" - "cada tabla debe tener una clave primaria" - verdadero - "e Identidad definida" - falso. La identidad y la clave primaria son conceptos ortogonales. Una columna de identidad no tiene que ser el PK de una tabla. Una clave primaria no tiene que ser una columna de identidad.
Damien_The_Unbeliever
OKAY. Mi concepto podría estar equivocado. Pero ahora he definido la estructura de la tabla con PK y Identity Seed. Si tengo que eliminar algunas filas, ¿cómo podría restablecer la semilla de identidad en un orden ascendente numérico correcto
Xorpower
29
Siempre diría que si te importan los valores numéricos reales generados en una columna de identidad, los estás usando mal. Lo único que debe importarle con una columna de identidad es que genera automáticamente valores únicos (¡sí!) Y que puede almacenar estos valores en una columna numérica (este bit solo es relevante para declarar columnas que contengan estos valores). No debería mostrárselos a nadie, por lo que no debería importar qué valores asuman.
Damien_The_Unbeliever
puede usar dbcc check identifique como otro mencionado pero tenga en cuenta que la clave primaria no es obligatoria para sql db v12
Satya_MSFT

Respuestas:

1099

El DBCC CHECKIDENTcomando de gestión se utiliza para restablecer el contador de identidad. La sintaxis del comando es:

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

Ejemplo:

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

No se admitía en versiones anteriores de Azure SQL Database, pero ahora se admite.


Tenga en cuenta que el new_reseed_valueargumento varía según las versiones de SQL Server según la documentación :

Si las filas están presentes en la tabla, la siguiente fila se inserta con el valor new_reseed_value . En la versión SQL Server 2008 R2 y anteriores, la siguiente fila insertada usa new_reseed_value + el valor de incremento actual.

Sin embargo, encuentro que esta información es engañosa (en realidad, simplemente es incorrecta) porque el comportamiento observado indica que al menos SQL Server 2012 todavía usa new_reseed_value + la lógica del valor de incremento actual. Microsoft incluso contradice con el suyo Example Cencontrado en la misma página:

C. Forzar el valor de identidad actual a un nuevo valor

El siguiente ejemplo fuerza el valor de identidad actual en la columna AddressTypeID en la tabla AddressType a un valor de 10. Debido a que la tabla tiene filas existentes, la siguiente fila insertada utilizará 11 como el valor, es decir, el nuevo valor de incremento actual definido para El valor de la columna más 1.

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

Aún así, todo esto deja una opción para un comportamiento diferente en las versiones más nuevas de SQL Server. Supongo que la única manera de estar seguro, hasta que Microsoft aclare las cosas en su propia documentación, es hacer pruebas reales antes de su uso.

Petr Abdulin
fuente
23
La sintaxis sería ... DBCC CHECKIDENT ('[TestTable]', RESEED, 0) GO
Biki
2
Parece que DBCC CHECKIDENTes compatible a partir de la próxima versión (V12 / Sterling): azure.microsoft.com/en-us/documentation/articles/… Aunque, para esta situación particular, aún recomendaría TRUNCATE TABLE :)
Solomon Rutzky
1
No funcionó para mí hasta que el "IR" estaba en otra línea.
mrówa
1
La sintaxis se está marcando debido a la palabra clave GO en la misma línea, no sé por qué. ¿Puedes moverlo por una línea? Copié y pegué esta línea 50 veces y ahora tengo que volver y arreglarla.
AnotherDeveloper
44
Funcionó perfectamente para mí. Vale la pena señalar que al volver a sembrar una tabla, si desea volver a sembrar para que su primer registro sea ID 1, entonces el comando de reposición debe restablecerse a 0, de modo que el siguiente registro sea ID 1.
Mike Upjohn
215
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

Donde 0 es el identityvalor inicial

anil shah
fuente
15
Si la tabla está vacía, como si acaba de llamar TRUNCATE, el nuevo valor inicial debe ser el valor para el próximo uso (es decir, 1 no 0). Si la tabla no está vacía, usará el new_reseed_value + 1. MSDN
kjbartel
2
@kjbartel, Anil y otros: no es tan simple como "si la tabla está vacía". En la documentación faltaba el caso para cuando la tabla está vacía debido a DELETE, no TRUNCATE, en cuyo caso también lo está new_reseed+value + 1. Escribí una publicación sobre esto, mostrando el comportamiento real a través de algunas pruebas, y actualicé el documento real (ahora que podemos debido a que está en GitHub): ¿Cómo funciona realmente DBCC CHECKIDENT al restablecer la semilla de identidad (RESEED)? .
Solomon Rutzky
87

Cabe señalar que SI todos los datos se eliminan de la tabla a través de DELETE(es decir, sin WHEREcláusula), siempre que a) los permisos lo permitan, yb) no hay FK que hagan referencia a la tabla (que parece ser el caso aquí), TRUNCATE TABLEse preferiría el uso, ya que es más eficiente DELETE y restablece la IDENTITYsemilla al mismo tiempo. Los siguientes detalles se toman de la página de MSDN para TRUNCATE TABLE :

En comparación con la declaración DELETE, TRUNCATE TABLE tiene las siguientes ventajas:

  • Se utiliza menos espacio en el registro de transacciones.

    La instrucción DELETE elimina las filas de una en una y registra una entrada en el registro de transacciones para cada fila eliminada. TRUNCATE TABLE elimina los datos al desasignar las páginas de datos utilizadas para almacenar los datos de la tabla y registra solo las desasignaciones de páginas en el registro de transacciones.

  • Por lo general, se usan menos cerraduras.

    Cuando la instrucción DELETE se ejecuta utilizando un bloqueo de fila, cada fila de la tabla se bloquea para su eliminación. TRUNCATE TABLE siempre bloquea la tabla (incluido un bloqueo de esquema (SCH-M)) y la página, pero no cada fila.

  • Sin excepción, quedan cero páginas en la tabla.

    Después de ejecutar una instrucción DELETE, la tabla aún puede contener páginas vacías. Por ejemplo, las páginas vacías en un montón no se pueden desasignar sin al menos un bloqueo de tabla exclusivo (LCK_M_X). Si la operación de eliminación no utiliza un bloqueo de tabla, la tabla (montón) contendrá muchas páginas vacías. Para los índices, la operación de eliminación puede dejar páginas vacías, aunque estas páginas se desasignarán rápidamente mediante un proceso de limpieza en segundo plano.

Si la tabla contiene una columna de identidad, el contador de esa columna se restablece al valor inicial definido para la columna. Si no se definió ninguna semilla, se usa el valor predeterminado 1. Para retener el contador de identidad, use DELETE en su lugar.

Entonces lo siguiente:

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

Se vuelve justo:

TRUNCATE TABLE [MyTable];

Consulte la TRUNCATE TABLEdocumentación (vinculada anteriormente) para obtener información adicional sobre restricciones, etc.

Solomon Rutzky
fuente
8
Si bien es más eficiente en las circunstancias correctas, esto no siempre es una opción. Truncar no se ejecutará en una tabla que tenga un FK definido en su contra. Incluso cuando no hay registros dependientes, truncar fallará si existe la restricción. También truncar requiere permisos ALTER donde Eliminar solo necesita DELETE.
Rozwel el
3
@Rozwel Es cierto, pero ya había calificado mi respuesta indicando que los permisos adecuados deben estar en su lugar. Además, la pregunta establece específicamente que no hay FK. Sin embargo, en aras de la claridad, actualicé para especificar la restricción "no FK". Gracias por señalar eso.
Solomon Rutzky
1
La única objeción es que cualquier FK bloqueará el truncamiento. Es posible (aunque inusual) tener un FK contra una restricción única que no es parte de las columnas PK o de identidad.
Rozwel el
1
@Rozwel Nuevamente es cierto, pero parece razonable suponer a partir de la pregunta que no existen restricciones únicas dado que la PK solo existe debido a la comprensión del OP (correcta o no) de que la base de datos Azure SQL la requiere. De todos modos, estoy a favor de reducir la ambigüedad, así que he actualizado nuevamente. Gracias.
Solomon Rutzky
No es tan inusual tener una clave foránea en una mesa, y la presencia de CUALQUIER clave foránea prohíbe TRUNCATE TABLE. Acabo de descubrir esto de la manera difícil hoy cuando intenté ejecutar TRUNCATE TABLE en una tabla que tiene una clave foránea que se aplica contra otras dos columnas en la tabla y un índice único en la tabla foránea.
David A. Gray
83

Aunque la mayoría de las respuestas sugieren RESEED a 0, pero muchas veces necesitamos volver a resetear al siguiente ID 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)

Esto verificará la tabla y se restablecerá a la siguiente ID.

Atal Kishore
fuente
2
Esta es la única respuesta que funciona el 100% del tiempo
Ingeniero invertido
3
Un poco más corto:declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
Guillermo Prandi
61

Intenté @anil shahsresponder y restableció la identidad. Pero cuando se insertó una nueva fila, obtuvo el identity = 2. Entonces, en cambio, cambié la sintaxis a:

DELETE FROM [TestTable]

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

Entonces la primera fila obtendrá la identidad = 1.

Mikael Engver
fuente
16

Aunque la mayoría de las respuestas están sugiriendo RESEEDa 0, y mientras algunos ven esto como un defecto de TRUNCATEDtablas, Microsoft tiene una solución que excluye laID

DBCC CHECKIDENT ('[TestTable]', RESEED)

Esto verificará la tabla y se restablecerá a la siguiente ID. Esto ha estado disponible desde MS SQL 2005 hasta la actualidad.

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

RealSollyM
fuente
1
Lamentablemente eso no es cierto. Acabo de comprobar eso para el servidor MS SQL 2014.
alehro
1
En realidad, es cierto para SQL 2014. Acabo de probarlo y funcionó para mí.
Daniel Dyson
2
Esto funciona de manera inconsistente para mí en SQL 2012. A veces usa el siguiente disponible como hubiera esperado, a veces parece atascarse en un valor antiguo de la tabla. Especificar la semilla siempre funciona.
Dan Field
No funciona para mí en SQL 2016, solo deja la semilla de identidad tal como está. Puede que haya funcionado correctamente para mí una vez, pero también podría haber sido mi problema con los dedos. No puedo hacer que funcione de nuevo
Ingeniero invertido
El mensaje indica éxito, Checking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.pero con nuevas inserciones sigue utilizando la semilla incorrecta.
Denziloe
7

emitir 2 comandos puede hacer el truco

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

el primero restablece la identidad a cero, y el siguiente lo establecerá en el siguiente valor disponible - jacob

jacob
fuente
2
DBCC CHECKIDENT ('[TestTable]', RESEED) no se está restableciendo al siguiente valor disponible
Atal Kishore
Este es el método utilizado por RedGate Data Compare cuando la opción "Restablecer columnas de identidad" está activada. Lo he probado exhaustivamente (quiero decir en código SQL, no en la herramienta RedGate), y funciona de manera confiable. (No tengo ninguna relación con RedGate aparte de ser un usuario ocasional de sus versiones de prueba)
Ingeniero invertido
6

@jacob

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

Funcionó para mí, solo tuve que borrar todas las entradas primero de la tabla, luego agregué lo anterior en un punto de activación después de eliminar. Ahora cada vez que borro una entrada se toma desde allí.

épico
fuente
DBCC CHECKIDENT solo funciona después de la eliminación. También podría usar truncar. Sin embargo, si necesita el resto de los datos, no lo use. También truncar no proporciona un recuento de registros eliminados.
user763539
6

Truncate Se prefiere la tabla porque borra los registros, restablece el contador y recupera el espacio en disco.

Deletey CheckIdentdebe usarse solo donde las claves externas evitan que se trunque.

Dyna Dave
fuente
5

Restablecer columna de identidad con nueva identificación ...

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

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

Esta es una pregunta común y la respuesta es siempre la misma: no lo hagas. Los valores de identidad deben tratarse como arbitrarios y, como tal, no existe un orden "correcto".

Ben Thul
fuente
15
Eso es cierto para un entorno de producción, pero durante el desarrollo me gusta recordar que ciertas entidades tienen un cierto Id, que se rellenan a partir de un script de inicialización. Hace que sea mucho más fácil navegar por la base de datos durante el desarrollo.
Francois Botha
77
Respuestas como esta son completamente teóricas y rara vez cumplen con las necesidades del mundo real. ¿Qué tal en lugar de lavarle el cerebro a las personas con tu dogma, respondes la pregunta de OP ...
Serj Sagan
1
Buena historia hermano. Mi opinión es la siguiente: si desea especificar el valor de una columna, no elija una propiedad en la columna que lo dificulte. El olor del código es el siguiente: si cada vez que inserta un registro en una tabla especifica un valor para la columna de identidad, no tiene una columna de identidad. El punto de identidad es que el servidor cree un valor para usted. Entonces, si anula esa vez, no ha ganado nada por un costo que no sea cero. Además, buen trabajo en el argumento ad hominem.
Ben Thul
55
Ciertamente estoy de acuerdo con tu opinión. Mirando el valor nominal, el OP ciertamente lo está haciendo mal, pero tal vez haya una necesidad más profunda que no se haya indicado en la publicación de que el OP no creía que fuera relevante para responder a su pregunta. Por lo tanto, responda la pregunta y brinde consejos de "qué hacer y qué no hacer" como parte de la respuesta. Por cierto, nunca ataqué a tu personaje ... ad hominem significa que te llamé estúpido o algo así ...
Serj Sagan
1
Si bien es cierto en la mayoría de los casos, existen circunstancias en las que es legítimo volver a sembrar una tabla. Por ejemplo, estoy trabajando en un proyecto greenfield que debe comenzar desde un punto determinado para tener en cuenta las filas existentes en el predecesor que está reemplazando. Volver a colocar durante el desarrollo es un caso de uso legítimo, IMO.
David A. Gray
3

Ejecute este script para restablecer la columna de identidad. Deberá hacer dos cambios. Reemplace tableXYZ con cualquier tabla que necesite actualizar. Además, el nombre de la columna de identidad debe eliminarse de la tabla temporal. Esto fue instantáneo en una mesa con 35,000 filas y 3 columnas. Obviamente, haga una copia de seguridad de la tabla y primero intente esto en un entorno de prueba.


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
Matthew Baic
fuente
3
Esto no es del todo correcto: SET IDENTITY_INSERT está en el lugar equivocado. No va alrededor del TRUNCADO, va alrededor del INSERT INTO (de ahí la identidad_ INSERT ). Además, esto se debe usar solo cuando los datos deben mantenerse, de lo contrario es muy ineficiente en comparación con solo ejecutar la única instrucción TRUNCATE.
Solomon Rutzky
1
DBCC CHECKIDENT (<TableName>, reseed, 0)

Esto establecerá el valor de identidad actual en 0.

Al insertar el siguiente valor, el valor de identidad se incrementa a 1.

Bimzee
fuente
1

Use este procedimiento almacenado:

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'

Solo revisando mi respuesta. Me encontré con un comportamiento extraño en SQL Server 2008 R2 que debe tener en cuenta.

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 primera selección produce 0, Item 1.

El segundo produce 1, Item 1. Si ejecuta el reinicio justo después de crear la tabla, el siguiente valor es 0. Honestamente, no me sorprende que Microsoft no pueda hacer esto correctamente. Lo descubrí porque tengo un archivo de script que llena las tablas de referencia que a veces ejecuto después de volver a crear tablas y, a veces, cuando las tablas ya están creadas.

costa
fuente
1

Yo uso el siguiente script para hacer esto. Solo hay un escenario en el que producirá un "error", que es si ha eliminado todas las filas de la tabla, y IDENT_CURRENTactualmente está configurado en 1, es decir, solo había una fila en la tabla para comenzar.

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)
;
Chris Mack
fuente
0

Para completar las filas DELETE y restablecer el recuento de IDENTIDAD, lo uso (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
Fandango68
fuente
0

Restablecer a 0 no es muy práctico a menos que esté limpiando la tabla como un todo.

de lo contrario, la respuesta dada por Anthony Raymond es perfecta. Obtenga el máximo de la columna de identidad primero, luego siembre con max.

Ali Sufyan
fuente
0

He estado tratando de hacer esto para una gran cantidad de tablas durante el desarrollo, y esto funciona de maravilla.

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

Entonces, primero lo fuerza a establecerse en 1, luego lo establece en el índice más alto de las filas presentes en la tabla. Descanso rápido y fácil del idex.

KimvdLinde
fuente
-2

Siempre es mejor usar TRUNCATE cuando sea posible en lugar de eliminar todos los registros, ya que tampoco usa espacio de registro.

En caso de que necesitemos eliminar y restablecer la semilla, recuerde siempre que si la tabla nunca se rellenó y usó DBCC CHECKIDENT('tablenem',RESEED,0) , el primer registro obtendrá identidad = 0 como se indica en la documentación de msdn

En su caso, solo reconstruya el índice y no se preocupe por perder la serie de identidad, ya que este es un escenario común.

Abdul Hannan Ijaz
fuente
3
Me parece que la idea es eliminar solo algunos registros.
Drumbeg
66
Esto es simplemente incorrecto: no es <i> SIEMPRE </i> mejor usar truncado y, de hecho, solo es mejor en algunos escenarios muy limitados y específicos. El cielo no permita que alguien siga tus consejos y luego deba retroceder.
Thronk
1
@ Thronk ¿Por qué estás insinuando que eso TRUNCATEevitaría que se ROLLBACKcomportara como se esperaba? ROLLBACK todavía retrocede. Incluso si el DB está configurado en BULK_LOGGED.
Solomon Rutzky
2
TRUNCATE es una operación DDL y no se registra en el archivo de registro. A menos que sea parte de la transacción (no se menciona en ninguna parte de la pregunta o en esta respuesta). Cada vez que alguien dice que algo es SIEMPRE cierto, es una apuesta bastante segura que están equivocados.
Thronk
Esta es la única respuesta que señala que hay una diferencia en el comportamiento RESEED dependiendo de si la secuencia se usó previamente o no. Una reinicialización del mismo valor en varias tablas vacías , donde algunas tablas se rellenaron previamente, dará como resultado valores iniciales diferentes para el primer registro insertado en cada tabla.
Simon Coleman
-4

Primero: Especificación de identidad Solo: "No" >> Guardar base de datos Ejecutar proyecto

Después de eso: Especificación de identidad Solo: "SÍ" >> Guardar base de datos Ejecutar proyecto

Su ID de base de datos, PK Comience desde 1 >>

Pratik Patel
fuente