¿Cómo cambiar los valores de las columnas de identidad mediante programación?

160

Tengo una base de datos MS SQL 2005 con una tabla Testcon columna ID. IDes una columna de identidad

Tengo filas en esta tabla y todas ellas tienen su correspondiente valor de incremento automático de ID.

Ahora me gustaría cambiar cada ID en esta tabla así:

ID = ID + 1

Pero cuando hago esto me sale un error:

No se puede actualizar la columna de identidad 'ID'.

He intentado esto:

    ALTER TABLE Test NOCHECK CONSTRAINT ALL 
    set identity_insert ID ON

Pero esto no resuelve el problema.

Necesito tener una identidad establecida en esta columna, pero también necesito cambiar los valores de vez en cuando. Entonces mi pregunta es cómo lograr esta tarea.

Tomasz Smykowski
fuente

Respuestas:

220

Necesitas

set identity_insert YourTable ON

Luego, elimine su fila y vuelva a insertarla con una identidad diferente.

Una vez que haya realizado la inserción, no olvide desactivar la identidad de inserción

set identity_insert YourTable OFF
Alaska
fuente
133
establecer esto funcionará solo cuando inserte datos y no cuando actualice. La declaración de ACTUALIZACIÓN seguirá fallando.
Husein Roncevic
31
Para una actualización, debe eliminar y volver a insertar. No hay otra manera.
cenizas999
1
@MartinSmith su solución parece demasiado larga. Poder hacerlo en dos pasos (identidad desactivada, eliminar / insertar, identidad activada) es mucho más eficaz.
cenizas999
3
@ ashes999 Eso es 4 pasos, no 2. Mi respuesta es solo una más (crear tabla, cambiar, actualizar, volver atrás, soltar) Es probable que esté más familiarizado con estos pasos para que se vean menos complicados. La actualización puede ser mucho más eficiente que eliminar la inserción cuando hay muchas filas involucradas. Además, ¿dónde tienes las filas eliminadas? Si una #temptabla que abandonas es otro paso que no has contado aquí.
Martin Smith
40

IDENTITY Los valores de columna son inmutables.

Sin embargo, es posible cambiar los metadatos de la tabla para eliminar la IDENTITYpropiedad, realizar la actualización y luego volver a cambiar.

Asumiendo la siguiente estructura

CREATE TABLE Test
(
ID INT IDENTITY(1,1) PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test 
OUTPUT INSERTED.*
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

Entonces puedes hacer

/*Define table with same structure but no IDENTITY*/
CREATE TABLE Temp
(
ID INT PRIMARY KEY,
X VARCHAR(10)
)

/*Switch table metadata to new structure*/
ALTER TABLE Test SWITCH TO Temp;

/*Do the update*/
UPDATE Temp SET ID = ID + 1;

/*Switch table metadata back*/
ALTER TABLE Temp SWITCH TO Test;

/*ID values have been updated*/
SELECT *
FROM Test

/*Safety check in case error in preceding step*/
IF NOT EXISTS(SELECT * FROM Temp)
    DROP TABLE Temp /*Drop obsolete table*/

En SQL Server 2012 es posible tener una columna de incremento automático que también puede actualizarse más directamente con SEQUENCES

CREATE SEQUENCE Seq
    AS INT
    START WITH 1
    INCREMENT BY 1

CREATE TABLE Test2
(
ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test2(X)
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

UPDATE Test2 SET ID+=1
Martin Smith
fuente
1
Muy hábil. Aprenda algo nuevo todos los días (Por cierto, probé esto en SQLExpress; a pesar de SWITCH TO, aparentemente no usa particiones). Tenga en cuenta que la tabla tiene que coincidir casi exactamente (índices, FK, etc.)
Mark Sowul
1
¿Funciona el método de cambio cuando tiene PK, FK, índice y vistas construidas sobre la tabla original? ¿Se romperán usando este método?
tj
1
@tj al crear el script de la tabla temporal fuera de la tabla fuente, incluidos todos los índices y restricciones. Luego cambie los nombres de la tabla y las restricciones para evitar colisiones. Las vistas no causarán problemas a menos que estén indexadas o relacionadas con el esquema.
Martin Smith
No funciona si hay un índice de búsqueda de texto completo en la tabla. Supongo que es posible eliminarlo y recrearlo después, pero no lo he probado.
youen
26

A través de la interfaz de usuario en el administrador de SQL Server 2005, cambie la columna, elimine la propiedad del número automático (identidad) de la columna (seleccione la tabla haciendo clic derecho sobre ella y elija "Diseño").

Luego ejecute su consulta:

UPDATE table SET Id = Id + 1

Luego vaya y agregue la propiedad de autonumeración nuevamente a la columna.

Michael Pryor
fuente
3
Si realiza el cambio manualmente, puede pedirle al administrador que genere el script SQL para el cambio (menú del diseñador de la tabla, generar script de cambio). Para este cambio, crea una nueva tabla y copia los datos, luego elimina el original.
Robin Bennett
2
@tomaszs: un ejemplo de código que también es más eficiente ya que no reconstruye físicamente la tabla (esto lo haría dos veces) está en mi respuesta tardía aquí
Martin Smith
No puede volver a agregar la identidad. ¿Vos si? stackoverflow.com/questions/1049210/…
Tomas Kubes
Gracias. Esto fue perfecto. Tenía 1 fila en mi tabla de la que no estaba al tanto, por lo que mis scripts de arranque para mi tabla tenían Idvalores de identidad automática desactivados en 1.
Shiva
18

En primer lugar, la configuración de IDENTITY_INSERT activada o desactivada para ese asunto no funcionará para lo que necesita (se utiliza para insertar nuevos valores, como tapar huecos).

Hacer la operación a través de la GUI solo crea una tabla temporal, copia todos los datos en una nueva tabla sin un campo de identidad y cambia el nombre de la tabla.

Miles D
fuente
9

Esto se puede hacer usando una tabla temporal.

La idea

  • desactivar restricciones (en caso de que una clave foránea haga referencia a su identificación)
  • crear una tabla temporal con la nueva identificación
  • eliminar el contenido de la tabla
  • copia los datos de la tabla copiada a su tabla original
  • habilitar restricciones previamente deshabilitadas

Consultas SQL

Digamos que su testtabla tiene dos columnas adicionales ( column2y column3) y que hay 2 mesas que tienen las claves externas que hacen referencia a testlos llamados foreign_table1y foreign_table2(debido a problemas de la vida real nunca son simples).

alter table test nocheck constraint all;
alter table foreign_table1 nocheck constraint all;
alter table foreign_table2 nocheck constraint all;
set identity_insert test on;

select id + 1 as id, column2, column3 into test_copy from test v;
delete from test;
insert into test(id, column2, column3)
select id, column2, column3 from test_copy

alter table test check constraint all;
alter table foreign_table1 check constraint all;
alter table foreign_table2 check constraint all;
set identity_insert test off;
drop table test_copy;

Eso es.

Manitra Andriamitondra
fuente
6

DBCC CHECKIDENT ('databasename.dbo.orders', RESEED, 999) puede cambiar cualquier número de columna de identidad con este comando, y también puede iniciar ese número de campo desde cada número que desee. Por ejemplo, en mi comando, pido comenzar desde 1000 (999 + 1) esperan que sea suficiente ... buena suerte

Roxana
fuente
4

Si la columna no es un PK, siempre puede crear una NUEVA columna en la tabla con los números incrementados, descartar el original y luego modificar el nuevo para que sea el antiguo.

curiosidad por saber por qué podría necesitar hacer esto ... la mayoría de las veces que he tenido que trabajar con columnas de Identidad fue rellenar números y acabé usando DBCC CHECKIDENT (nombre de tabla, RESEED, newnextnumber)

¡buena suerte!

Christopher Klein
fuente
1

La modificación de la identidad puede fallar dependiendo de una serie de factores, principalmente en torno a los objetos / relaciones vinculados a la columna de identificación. Parece que el diseño de db es un problema aquí, ya que los ID rara vez deberían cambiar (estoy seguro de que tiene sus razones y está cambiando los cambios). Si realmente necesita cambiar las identificaciones de vez en cuando, le sugiero que cree una nueva columna de identificación ficticia que no sea la clave principal / número automático que puede administrar usted mismo y generar a partir de los valores actuales. Alternativamente, la idea de Chrisotphers anterior sería mi otra sugerencia si tiene problemas para permitir la inserción de identidad.

Buena suerte

PD: no falla porque el orden secuencial en el que se está ejecutando está tratando de actualizar un valor de la lista a un elemento que ya existe en la lista de identificadores. agarrando las pajitas, quizás agregue el número de filas + 1, luego, si eso funciona, reste el número de filas: -S

Curtidor
fuente
1

Si necesita cambiar las ID ocasionalmente, probablemente sea mejor no usar una columna de identidad. En el pasado, implementamos campos de numeración automática manualmente usando una tabla de 'Contadores' que rastrea la siguiente ID para cada tabla. IIRC lo hicimos porque las columnas de identidad estaban causando daños en la base de datos en SQL2000, pero poder cambiar los ID ocasionalmente era útil para las pruebas.

Robin Bennett
fuente
1

Puede insertar nuevas filas con valores modificados y luego eliminar las viejas. El siguiente ejemplo cambia la ID para que sea igual a la clave externa PersonId

SET IDENTITY_INSERT [PersonApiLogin] ON

INSERT INTO [PersonApiLogin](
       [Id]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess])
SELECT [PersonId]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess]
FROM [db304].[dbo].[PersonApiLogin]
GO

DELETE FROM [PersonApiLogin]
WHERE [PersonId] <> ID
GO
SET IDENTITY_INSERT [PersonApiLogin] OFF
GO
Tomás Kubes
fuente
1

Primero guarde todas las ID y modifíquelas programáticamente a los valores que desee, luego quítelos de la base de datos y luego insértelos nuevamente usando algo similar:

use [Name.Database]
go
set identity_insert [Test] ON
insert into [dbo].[Test]
           ([Id])
     VALUES
           (2)
set identity_insert [Test] OFF

Para uso a granel:

use [Name.Database]
go
set identity_insert [Test] ON
BULK INSERT [Test]
FROM 'C:\Users\Oscar\file.csv'
WITH (FIELDTERMINATOR = ';',
      ROWTERMINATOR = '\n',
      KEEPIDENTITY)
set identity_insert [Test] OFF

Datos de muestra de file.csv:

2;
3;
4;
5;
6;

Si no lo configura identity_insert, obtendrá el siguiente error:

No se puede insertar un valor explícito para la columna de identidad en la tabla 'Prueba' cuando IDENTITY_INSERT está establecido en OFF.

Ogglas
fuente
0

Vi un buen artículo que me ayudó en el último momento. Intenté insertar algunas filas en una tabla que tenía una columna de identidad, pero lo hice mal y tuve que borrarla. Una vez que eliminé las filas, mi columna de identidad cambió. Estaba tratando de encontrar una manera de actualizar la columna que se insertó, pero no tuve suerte. Entonces, mientras buscaba en google encontró un enlace ...

  1. Se eliminaron las columnas que se insertaron incorrectamente.
  2. Usar inserción forzada utilizando identidad activada / desactivada (se explica a continuación)

http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do- i-update-the-value-of-an.aspx

Balu
fuente
1
Sin embargo, no estoy seguro de que hayas respondido la pregunta aquí.
Andrew Barber
0

Muy buena pregunta, primero necesitamos en IDENTITY_INSERT para la tabla específica, luego ejecutar la consulta de inserción (Debe especificar el nombre de la columna).

Nota: Después de editar la columna de identidad, no olvide desactivar IDENTITY_INSERT. Si no lo ha hecho, no podrá editar la columna de identidad para ninguna otra tabla.

SET IDENTITY_INSERT Emp_tb_gb_Menu ON
     INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
SET IDENTITY_INSERT Emp_tb_gb_Menu OFF

http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

Asith Raj
fuente