Un valor explícito para la columna de identidad en la tabla solo se puede especificar cuando se usa una lista de columnas y IDENTITY_INSERT está EN SQL Server

187

Estoy tratando de hacer esta consulta

INSERT INTO dbo.tbl_A_archive
  SELECT *
  FROM SERVER0031.DB.dbo.tbl_A

pero incluso después de correr

set identity_insert dbo.tbl_A_archive on

Recibo este mensaje de error

Un valor explícito para la columna de identidad en la tabla 'dbo.tbl_A_archive' solo se puede especificar cuando se utiliza una lista de columnas y IDENTITY_INSERT está ACTIVADO.

tbl_Aes una tabla enorme en filas y ancho, es decir, tiene MUCHAS columnas. No quiero tener que escribir todas las columnas manualmente. ¿Cómo puedo hacer que esto funcione?

jhowe
fuente
Por cierto, ya he configurado un servidor vinculado.
jhowe
44
Estoy teniendo este problema, así, a excepción del "inserto en X * Y seleccione" no era un problema hasta que me cambió el esquema de tabla
FistOfFury

Respuestas:

81

Resumen

SQL Server no le permitirá insertar un valor explícito en una columna de identidad a menos que use una lista de columnas. Por lo tanto, tiene las siguientes opciones:

  1. Haga una lista de columnas (ya sea manualmente o usando herramientas, ver más abajo)

O

  1. haga la columna de identidad en tbl_A_archiveuna columna normal, sin identidad : si su tabla es una tabla de archivo y siempre especifica un valor explícito para la columna de identidad, ¿por qué necesita una columna de identidad? Solo usa un int regular en su lugar.

Detalles sobre la solución 1

En vez de

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table
  SELECT *
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

necesitas escribir

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table (field1, field2, ...)
  SELECT field1, field2, ...
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

con field1, field2, ...contener los nombres de todas las columnas en sus tablas. Si desea generar automáticamente esa lista de columnas, eche un vistazo a la respuesta de Dave o la respuesta de Andomar .


Detalles sobre la solución 2

Desafortunadamente, no es posible simplemente "cambiar el tipo" de una columna int de identidad a una columna int sin identidad. Básicamente, tiene las siguientes opciones:

  • Si la tabla de archivo todavía no contiene datos, suelte la columna y agregue una nueva sin identidad.

O

  • Use SQL Server Management Studio para establecer la propiedad Identity Specification/ (Is Identity)de la columna de identidad en su tabla de archivo No. Detrás de escena, esto creará una secuencia de comandos para volver a crear la tabla y copiar los datos existentes, por lo que, para hacerlo, también deberá desarmar Tools/ Options/ Designers/ Table and Database Designers/ Prevent saving changes that require table re-creation.

O

Heinzi
fuente
1
Para su solución final: si la columna tiene "IDENTIDAD (1, 1)" o algo así, también deberá eliminarla temporalmente.
Aleksandr Khomenko
1
@AleksandrKhomenko: Sí, eso es lo que quise decir con "hacer que [sea] una columna int regular ( sin identidad )" .
Heinzi
1
Perdón por la confusion. En realidad, quise decir que también necesitará eliminar la propiedad de incremento automático . En el caso de SQL-Server, se llama "IDENTIDAD (1, 1)": su respuesta es absolutamente correcta. Pero MySQL y Oracle tienen otros comandos para ello (y se vuelve no obvio, por favor mire w3schools.com/sql/sql_autoincrement.asp )
Aleksandr Khomenko
2
Esto carece de detalles. Entiendo que puede ser de memoria para algunos, pero no tiene sentido para otros
DJ.
1
@DJ .: He agregado algunos detalles.
Heinzi
332
SET IDENTITY_INSERT tableA ON

Debe hacer una lista de columnas para su instrucción INSERT:

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

no como "INSERTAR en la tabla A SELECCIONAR ........"

SET IDENTITY_INSERT tableA OFF
Ehsan
fuente
17
+1 ... Solo necesita columnas explícitas en la cláusula SELECT de la consulta. Puede mantener un asterisco en la cláusula INSERT de la consulta
MacGyver
8
... a menos que el objetivo tenga una columna de identidad y la tabla de origen no tenga una columna de identidad
MacGyver
1
esta respuesta es un poco más clara que Heinzi porque menciona SET IDENTITY_INSERT
Marty
44
¡+1 funciona como un encanto! Sin embargo, "Puede mantener un asterisco en la cláusula INSERT de la consulta" no funcionará aquí.
Shai Alon
1
Recibí este error cuando las columnas en la cláusula select no coincidían con la tabla de destino. Asegurándome de esto, el inserto funcionó para mí
José,
39

Si está utilizando SQL Server Management Studio, no tiene que escribir la lista de columnas usted mismo: simplemente haga clic con el botón derecho en la tabla en el Explorador de objetos y elija Tabla de secuencias de comandos como -> SELECCIONAR para -> Nueva ventana del editor de consultas .

Si no lo está, una consulta similar a esta debería ayudar como punto de partida:

SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'tbl_A'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);
Dave Cluderay
fuente
2
Esto puede seleccionarse en una variable y usarse en consultas SQL dinámicas. Salvaste mi día. ¡Muchas gracias!
Pawel Cioch
1
¡Gracias! Hice esto con él gist.github.com/timabell/0ddd6a69565593f907c7
Tim Abell el
Guau. Estaba desesperadamente buscando esto para construir mi consulta dinámicamente. Sorprendido de ver esto aquí. Gracias amigo.
Yasin Bilir
24

De acuerdo con la respuesta de Heinzi. Para la opción del primer segundo, aquí hay una consulta que genera una lista de columnas separadas por comas en una tabla:

select name + ', ' as [text()] 
from sys.columns 
where object_id = object_id('YourTable') 
for xml path('')

Para tablas grandes, esto puede ahorrar mucho trabajo de escritura :)

Andomar
fuente
@ Gracias, realmente útil. :)
Chirag Thakar
A veces solo necesitas una solución pragmática. ¡Gracias!
Tobias Feil
15

Si la tabla "archivo" está destinada a ser una copia exacta de su tabla principal, entonces le sugiero que elimine el hecho de que la identificación es una columna de identidad. De esa manera te permitirá insertarlos.

Alternativamente, puede permitir y no permitir las inserciones de identidad para la tabla con la siguiente instrucción

SET IDENTITY_INSERT tbl_A_archive ON
--Your inserts here
SET IDENTITY_INSERT tbl_A_archive OFF

Finalmente, si necesita que la columna de identidad funcione como está, siempre puede ejecutar el proceso almacenado.

sp_columns tbl_A_archive 

Esto le devolverá todas las columnas de la tabla que luego puede cortar y pegar en su consulta. (Esto es SIEMPRE mejor que usar un *)

Día de robin
fuente
11

Para la instrucción SQL, también debe especificar la lista de columnas. Por ej.

INSERT INTO tbl (idcol1,col2) VALUES ( value1,value2)

en vez de

INSERT INTO tbl VALUES ( value1,value2)
Mohsin Mahmood
fuente
2
Este no es el caso de la declaración del OP. VALUES no se requiere si INSERT INTO es seguido por una instrucción SELECT. Por favor revise o elimine su respuesta.
Gary
4

Ambos funcionarán, pero si aún obtiene un error al usar el n. ° 1, vaya al n. ° 2

1)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
SELECT Id, ...
FROM SERVER0031.DB.dbo.tbl_A

2)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
VALUES(@Id,....)
Chirag Thakar
fuente
44
Este realmente no es el camino a seguir si el OP está insertando múltiples registros. Estás ignorando que "tbl_A es una tabla enorme en filas y ancho, es decir, tiene MUCHAS columnas. No quiero tener que escribir todas las columnas manualmente".
Gary
4

Para completar todos los nombres de columna en una lista delimitada por comas para una instrucción Select para las soluciones mencionadas para esta pregunta, utilizo las siguientes opciones, ya que son un poco menos detalladas que la mayoría de las respuestas aquí. Sin embargo, la mayoría de las respuestas aquí todavía son perfectamente aceptables.

1)

SELECT column_name + ',' 
FROM   information_schema.columns 
WHERE  table_name = 'YourTable'

2) Este es probablemente el enfoque más simple para crear columnas, si tiene SSMS de SQL Server.

1) Vaya a la tabla en el Explorador de objetos y haga clic en el signo + a la izquierda del nombre de la tabla o haga doble clic en el nombre de la tabla para abrir la sublista.

2) Arrastre la subcarpeta de la columna al área de consulta principal y automáticamente pegará la lista de columnas completa por usted.

Ryan
fuente
3

Debe especificar el nombre de las columnas que desea insertar si hay una columna de identidad. Entonces el comando será así a continuación:

SET IDENTITY_INSERT DuplicateTable ON

INSERT Into DuplicateTable ([IdentityColumn], [Column2], [Column3], [Column4] ) 
SELECT [IdentityColumn], [Column2], [Column3], [Column4] FROM MainTable

SET IDENTITY_INSERT DuplicateTable OFF

Si su tabla tiene muchas columnas, obtenga el nombre de esas columnas con este comando.

SELECT column_name + ','
FROM   information_schema.columns 
WHERE  table_name = 'TableName'
for xml path('')

(después de eliminar la última coma (',')) Simplemente copie el nombre de las columnas anteriores.

Rousonur Jaman
fuente
3

Esto debería funcionar. Acabo de encontrarme con tu problema:

SET IDENTITY_INSERT dbo.tbl_A_archive ON;
INSERT INTO     dbo.tbl_A_archive (IdColumn,OtherColumn1,OtherColumn2,...)
SELECT  *
FROM        SERVER0031.DB.dbo.tbl_A;
SET IDENTITY_INSERT dbo.tbl_A_archive OFF;

Desafortunadamente, parece que necesita una lista de las columnas, incluida la columna de identidad, para insertar registros que especifiquen la identidad. Sin embargo , NO TIENE que enumerar las columnas en SELECT. Como sugirió @Dave Cluderay, esto dará como resultado una lista formateada para que copie y pegue (si tiene menos de 200000 caracteres).

Agregué USE porque estoy cambiando entre instancias.

USE PES
SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'Provider'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);
Gary
fuente
3
SET IDENTITY_INSERT tableA ON

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

Así no

INSERT INTO tableA
SELECT * FROM tableB

SET IDENTITY_INSERT tableA OFF
Conejito
fuente
2

Este fragmento de código muestra cómo insertar en la tabla cuando la columna de clave primaria de identidad está activada.

SET IDENTITY_INSERT [dbo].[Roles] ON
GO
insert into Roles (Id,Name) values(1,'Admin')
GO
insert into Roles (Id,Name) values(2,'User')
GO
SET IDENTITY_INSERT [dbo].[Roles] OFF
GO
AminGolmahalle
fuente
1

Por si desea insertar sus valores de una tabla a otra a través de un procedimiento almacenado. He utilizado este y este , el último, que es casi como la respuesta de Andomar.

CREATE procedure [dbo].[RealTableMergeFromTemp]
    with execute as owner
AS
BEGIN
BEGIN TRANSACTION RealTableDataMerge
SET XACT_ABORT ON

    DECLARE @columnNameList nvarchar(MAX) =
     STUFF((select ',' + a.name
      from sys.all_columns a
      join sys.tables t on a.object_id = t.object_id 
       where t.object_id = object_id('[dbo].[RealTable]') 
    order by a.column_id
    for xml path ('')
    ),1,1,'')

    DECLARE @SQLCMD nvarchar(MAX) =N'INSERT INTO [dbo].[RealTable] (' + @columnNameList + N') SELECT * FROM [#Temp]'

    SET IDENTITY_INSERT [dbo].[RealTable] ON;
    exec(@sqlcmd)
    SET IDENTITY_INSERT [dbo].[RealTable] OFF

COMMIT TRANSACTION RealTableDataMerge
END

GO
Rubenismo
fuente
0

Hay una o más columnas que tienen la propiedad de incremento automático o el valor de ese atributo se calculará como restricciones. Estás intentando modificar esa columna.

Hay dos maneras de resolverlo 1) Mencione explícitamente otras columnas y establezca sus valores solamente y la clave primaria o el valor de columna de incremento automático se establecerán automáticamente.

2) Puede activar INDENTITY_INSERT luego ejecutar su consulta de inserción y finalmente desactivar IDENTITY_INSERT.

Sugerencia: siga el primer paso porque es un enfoque más adecuado y eficiente.

Para obtener más información, lea este artículo sobre SQL-helper .

Shanu Dey
fuente
0

Asegúrese de que los nombres de columna, los tipos de datos y el orden en la tabla desde la que selecciona los registros sean exactamente los mismos que en la tabla de destino. La única diferencia debería ser que la tabla de destino tiene una columna de identidad como la primera columna, que no está allí en la tabla de origen.

Estaba enfrentando un problema similar cuando estaba ejecutando "INSERT INTO table_Dest SELECT * FROM table_source_linked_server_excel". Las mesas tenían 115 columnas.

Tenía dos tablas de este tipo donde estaba cargando datos de Excel (como servidor vinculado) en tablas en la base de datos. En las tablas de la base de datos, había agregado una columna de identidad llamada 'id' que no estaba allí en Excel de origen. Para una tabla, la consulta se ejecutó correctamente y en otra recibí el error "Un valor explícito para la columna de identidad en la tabla solo se puede especificar cuando se usa una lista de columnas y IDENTITY_INSERT está en SQL Server". Esto fue desconcertante ya que el escenario era exactamente el mismo para ambas consultas. Así que investigué esto y lo que encontré fue que en la consulta donde recibía un error con INSERT INTO ... SELECT *:

  1. Se modificaron algunos de los nombres de columna en la tabla fuente, aunque los valores eran correctos
  2. Hubo algunas columnas adicionales más allá de las columnas de datos reales que fueron seleccionadas por SELECT *. Descubrí esto usando la opción de "Tabla de scripts como> Seleccionar para> nueva ventana de consulta" en la tabla de Excel de origen (en servidores vinculados). Había una columna oculta justo después de la última columna en Excel, aunque no tenía ningún dato. Eliminé esa columna en la tabla de origen de Excel y la guardé.

Después de hacer los dos cambios anteriores, la consulta INSERT INTO ... SELECT * se ejecutó correctamente. La columna de identidad en la tabla de destino generó valores de identidad para cada fila insertada como se esperaba.

Por lo tanto, aunque la tabla de destino puede tener una columna de identidad que no está en la tabla de origen, INSERT INTO ... SELECT * se ejecutará correctamente si los nombres, los tipos de datos y el orden de las columnas en el origen y el destino son exactamente los mismos.

Espero que ayude a alguien.

Uttam
fuente
-1

Creo que este error ocurre debido a la falta de coincidencia con el número de columnas en la definición de la tabla y el número de columnas en la consulta de inserción. Además, la longitud de la columna se omite con el valor introducido. Así que solo revise la definición de la tabla para resolver este problema

Codemaker
fuente
A mí me funcionó: olvidé agregar una columna a la tabla desde el diseño, lo bueno es verificar esto primero antes de intentar otras respuestas.
Exel Gamboa