Um valor explícito para a coluna de identidade na tabela só pode ser especificado quando uma lista de colunas é usada e IDENTITY_INSERT está ativado no SQL Server

187

Estou tentando fazer essa consulta

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

mas mesmo depois que eu corri

set identity_insert dbo.tbl_A_archive on

Estou recebendo esta mensagem de erro

Um valor explícito para a coluna de identidade na tabela 'dbo.tbl_A_archive' só pode ser especificado quando uma lista de colunas é usada e IDENTITY_INSERT está ativado.

tbl_Aé uma tabela enorme em linhas e largura, ou seja, tem muitas colunas. Não quero digitar todas as colunas manualmente. Como posso fazer isso funcionar?

jhowe
fonte
Eu já configurei um servidor vinculado por sinal!
jhowe
4
estou tendo este problema, bem como, excepto a "inserção em X seleccionar * Y" não foi um problema até i mudado o esquema da tabela
FistOfFury

Respostas:

81

Resumo

O SQL Server não permitirá que você insira um valor explícito em uma coluna de identidade, a menos que você use uma lista de colunas. Assim, você tem as seguintes opções:

  1. Faça uma lista de colunas (manualmente ou usando ferramentas, veja abaixo)

OU

  1. torne a coluna de identidade em tbl_A_archiveuma coluna regular, sem identidade : se sua tabela é uma tabela de arquivamento e você sempre especifica um valor explícito para a coluna de identidade, por que você precisa de uma coluna de identidade? Basta usar um int regular.

Detalhes sobre a solução 1

Ao invés de

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table
  SELECT *
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

você precisa escrever

SET IDENTITY_INSERT archive_table ON;

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

SET IDENTITY_INSERT archive_table OFF;

com field1, field2, ...contendo os nomes de todas as colunas em suas tabelas. Se você deseja gerar automaticamente essa lista de colunas, veja a resposta de Dave ou Andomar .


Detalhes sobre a solução 2

Infelizmente, não é possível apenas "alterar o tipo" de uma coluna int de identidade para uma coluna int de não identidade. Basicamente, você tem as seguintes opções:

  • Se a tabela de arquivamento ainda não contiver dados, solte a coluna e adicione uma nova sem identidade.

OU

  • Use o SQL Server Management Studio para definir a propriedade Identity Specification/ (Is Identity)da coluna de identidade na sua tabela de arquivamento No. Nos bastidores, isso criará um script para recriar a tabela e copiar os dados existentes; portanto, para isso, você também precisará desmarcar Tools/ Options/ Designers/ Table and Database Designers/ Prevent saving changes that require table re-creation.

OU

Heinzi
fonte
1
Para sua solução inferior: se a coluna tiver "IDENTITY (1, 1)" ou algo parecido com isso, ele precisará removê-la temporariamente também.
Aleksandr Khomenko
1
@AleksandrKhomenko: Sim, é isso que eu quis dizer com "tornar [isso] uma coluna int regular ( sem identidade )" .
Heinzi
1
Desculpe pela confusão. Na verdade, eu quis dizer que ele também precisará remover a propriedade de incremento automático . No caso do SQL Server, ele chama "IDENTITY (1, 1)" - sua resposta está absolutamente correta. Mas MySQL e Oracle tem outros comandos para ele (e ele fica não-óbvia, por favor, olhe para w3schools.com/sql/sql_autoincrement.asp )
Aleksandr Khomenko
2
Isso está faltando em detalhes. Eu entendo que pode ser importante para alguns, mas não faz sentido para outros
DJ.
1
@ DJ: eu adicionei alguns detalhes.
Heinzi 19/06
332
SET IDENTITY_INSERT tableA ON

Você precisa fazer uma lista de colunas para sua instrução INSERT:

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

não é como "INSERIR na tabelaA SELECT ........"

SET IDENTITY_INSERT tableA OFF
Ehsan
fonte
17
+1 ... Você só precisa de colunas explícitas na cláusula SELECT da consulta. Você pode manter um asterisco na cláusula INSERT da consulta
MacGyver
8
... a menos que o alvo tem uma coluna de identidade, e a tabela de origem não tem uma coluna de identidade
MacGyver
1
esta resposta é um pouco mais claro do que Heinzi porque menciona SET IDENTITY_INSERT
Marty
4
+1 Funciona como um encanto! No entanto, "Você pode manter um asterisco na cláusula INSERT da consulta" não funcionará aqui.
Shai Alon
1
Eu recebi esse erro quando as colunas na cláusula de seleção não correspondiam à tabela de destino. Certificando-se a isso, a inserção funcionou para mim
Jose
39

Se você estiver usando o SQL Server Management Studio, não precisará digitar a lista de colunas - basta clicar com o botão direito do mouse na tabela no Pesquisador de Objetos e escolher Tabela de Scripts como -> SELECIONAR para -> Nova Janela do Editor de Consultas .

Se não estiver, uma consulta semelhante a esta deve ajudar como ponto 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
fonte
2
Isso pode ser selecionado em uma variável e usado na consulta SQL dinâmica. Você salvou o meu dia. Muito obrigado!
Pawel Cioch
1
obrigado! Eu fiz isso com ele gist.github.com/timabell/0ddd6a69565593f907c7
Tim Abell
Uau. Eu estava desesperadamente procurando por isso para criar minha consulta dinamicamente. Surpreso ao ver isso aqui. Obrigado companheiro.
Yasin Bilir
24

Concorde com a resposta de Heinzi. Para a primeira segunda opção, veja uma consulta que gera uma lista de colunas separada por vírgula em uma tabela:

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

Para tabelas grandes, isso pode economizar muito trabalho de digitação :)

Andomar
fonte
@Obrigado, realmente útil. :)
Chirag Thakar 30/03
Às vezes você só precisa de uma solução pragmática. Obrigado!
Tobias Feil
15

Se a tabela "archive" for uma cópia exata da tabela principal, sugiro que você remova o fato de que o id é uma coluna de identificação. Dessa forma, você poderá inseri-los.

Como alternativa, você pode permitir e a identidade não permitida é inserida na tabela com a seguinte instrução

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

Por fim, se você precisar que a coluna de identidade funcione como está, sempre poderá executar o processo armazenado.

sp_columns tbl_A_archive 

Isso retornará todas as colunas da tabela, que você poderá recortar e colar na sua consulta. (Isso é quase sempre melhor do que usar um *)

Robin Day
fonte
11

Para a instrução SQL, você também deve especificar a lista de colunas. Por exemplo.

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

ao invés de

INSERT INTO tbl VALUES ( value1,value2)
Mohsin Mahmood
fonte
2
Este não é o caso da declaração do OP. VALUES não é necessário se INSERT INTO for seguido por uma instrução SELECT. Revise ou remova sua resposta.
Gary
4

Ambos funcionarão, mas se você ainda receber um erro usando o número 1, vá para o número 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
fonte
4
Este realmente não é o caminho a seguir se o OP estiver inserindo vários registros. Você está ignorando "tbl_A é uma tabela enorme em linhas e largura, ou seja, tem muitas colunas. Não quero digitar todas as colunas manualmente."
Gary
4

Para preencher todos os nomes de colunas em uma lista delimitada por vírgula para uma instrução Select para as soluções mencionadas para esta pergunta, eu uso as seguintes opções, pois elas são um pouco menos detalhadas do que a maioria das respostas aqui. Embora, a maioria das respostas aqui ainda sejam perfeitamente aceitáveis, no entanto.

1)

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

2) Essa é provavelmente a abordagem mais simples para criar colunas, se você tiver o SQL Server SSMS.

1) Vá para a tabela no Pesquisador de Objetos e clique no + à esquerda do nome da tabela ou clique duas vezes no nome da tabela para abrir a sub-lista.

2) Arraste a subpasta da coluna para a área de consulta principal e ela fará o autopaste de toda a lista de colunas para você.

Ryan
fonte
3

Você deve especificar o nome das colunas que deseja inserir se houver uma coluna Identidade. Portanto, o comando será assim abaixo:

SET IDENTITY_INSERT DuplicateTable ON

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

SET IDENTITY_INSERT DuplicateTable OFF

Se sua tabela tiver muitas colunas, obtenha o nome dessas colunas usando este comando.

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

(após remover a última vírgula (',')) Basta copiar o nome das colunas anteriores.

Rousonur Jaman
fonte
3

Isso deve funcionar. Acabei de encontrar seu 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;

Infelizmente, parece que você precisa de uma lista de colunas, incluindo a coluna de identidade, para inserir registros que especificam a identidade. No entanto , você não precisa listar as colunas no SELECT. Como o @Dave Cluderay sugeriu, isso resultará em uma lista formatada para você copiar e colar (se tiver menos de 200000 caracteres).

Eu adicionei o USE, pois estou alternando entre instâncias.

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
fonte
3
SET IDENTITY_INSERT tableA ON

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

Assim não

INSERT INTO tableA
SELECT * FROM tableB

SET IDENTITY_INSERT tableA OFF
Coelho
fonte
2

Este trecho de código mostra como inserir na tabela quando a coluna Chave Primária da identidade está LIGADA.

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
fonte
1

Pois se você deseja inserir seus valores de uma tabela para outra através de um procedimento armazenado. Eu usei isso e isso , o último, que é quase como a resposta 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
Rubenisme
fonte
0

Há uma ou mais colunas que possuem propriedade de incremento automático ou o valor desse atributo será calculado como restrições. Você está tentando modificar essa coluna.

Há duas maneiras de resolvê-lo: 1) Mencione outras colunas explicitamente e defina apenas seus valores, e o valor PrimaryKey ou a coluna de incremento automático serão definidos automaticamente.

2) Você pode ativar INDENTITY_INSERT e, em seguida, executar sua consulta de inserção, desative IDENTITY_INSERT.

Sugestão: Siga o primeiro passo, porque é uma abordagem mais adequada e eficiente.

Para mais informações, leia este artigo no SQL-helper .

Shanu Dey
fonte
0

Certifique-se de que os nomes das colunas, tipos de dados e ordem na tabela de onde você está selecionando registros sejam exatamente iguais à tabela de destino. A única diferença deve ser que a tabela de destino possui uma coluna de identidade como a primeira coluna, que não existe na tabela de origem.

Eu estava enfrentando um problema semelhante ao executar "INSERT INTO table_Dest SELECT * FROM table_source_linked_server_excel". As tabelas tinham 115 colunas.

Eu tinha duas tabelas em que estava carregando dados do Excel (como servidor vinculado) em tabelas no banco de dados. Nas tabelas do banco de dados, eu adicionei uma coluna de identidade chamada 'id' que não estava lá no Excel de origem. Para uma tabela, a consulta foi executada com êxito e, em outra, recebi o erro "Um valor explícito para a coluna de identidade na tabela só pode ser especificado quando uma lista de colunas é usada e IDENTITY_INSERT está no SQL Server". Isso foi intrigante, pois o cenário era exatamente o mesmo para as duas consultas. Então, investiguei isso e o que descobri foi que, na consulta em que estava recebendo erro com INSERT INTO .. ​​SELECT *:

  1. Alguns dos nomes de coluna na tabela de origem foram modificados, embora os valores estivessem corretos
  2. Havia algumas colunas extras além das colunas de dados reais que estavam sendo selecionadas por SELECT *. Descobri isso usando a opção "Tabela de scripts como> Selecionar para> nova janela de consulta" na tabela do Excel de origem (em servidores vinculados). Havia uma coluna oculta logo após a última coluna no Excel, embora não tivesse nenhum dado. Excluí essa coluna na tabela de origem do Excel e a salvei.

Depois de fazer as duas alterações acima, a consulta INSERT INTO ... SELECT * foi executada com êxito. A coluna de identidade na tabela de destino gerou valores de identidade para cada linha inserida conforme o esperado.

Portanto, mesmo que a tabela de destino possa ter uma coluna de identidade que não existe na tabela de origem, o INSERT INTO .. ​​SELECT * será executado com êxito se os nomes, tipos de dados e ordem das colunas na origem e no destino forem exatamente os mesmos.

Espero que ajude alguém.

Uttam
fonte
-1

Acho que esse erro ocorre devido à incompatibilidade com o número de colunas na definição da tabela e o número de colunas na consulta de inserção. Além disso, o comprimento da coluna é omitido com o valor inserido. Portanto, basta revisar a definição da tabela para resolver esse problema

Codemaker
fonte
Trabalhou para mim: esqueci-me de adicionar uma coluna à tabela a partir do design, bom verificar isso antes de tentar outras respostas.
Exel Gamboa