Como você copia um registro em uma tabela SQL, mas troca o ID exclusivo da nova linha?

123

Essa pergunta chega perto do que eu preciso, mas meu cenário é um pouco diferente. A tabela de origem e a tabela de destino são as mesmas e a chave primária é um identificador exclusivo (guid). Quando eu tento isso:

insert into MyTable
    select * from MyTable where uniqueId = @Id;

Obviamente, recebo uma violação de restrição de chave primária, pois estou tentando copiar a chave primária. Na verdade, não quero copiar a chave primária. Em vez disso, quero criar um novo. Além disso, gostaria de copiar seletivamente sobre determinados campos e deixar os outros nulos. Para tornar as coisas mais complexas, preciso pegar a chave primária do registro original e inseri-la em outro campo da cópia (campo PreviousId).

Tenho certeza de que existe uma solução fácil para isso, simplesmente não conheço TSQL suficiente para saber o que é.

Kilhoffer
fonte

Respostas:

186

Tente o seguinte:


insert into MyTable(field1, field2, id_backup)
    select field1, field2, uniqueId from MyTable where uniqueId = @Id;

Quaisquer campos não especificados devem receber seu valor padrão (que geralmente é NULL quando não definido).

AaronSieb
fonte
2
Impressionante. Funcionou como um encanto. Era tão óbvio quando você escrevia. Obrigado!!
Kilhoffer
1
Isso funcionará se você tiver poucas colunas. Se você disser 20 ou 30 colunas, ainda poderá usar esse método, mas seria assustador. Além disso, sempre que você adiciona uma coluna e também deseja copiar esses dados, é necessário adicionar a coluna a esse script. O melhor é gerar inserção dinamicamente usando tabelas sys.
Jeyara
93

Ok, eu sei que é um problema antigo, mas eu posto minha resposta de qualquer maneira.

Eu gosto desta solução. Eu só tenho que especificar a (s) coluna (s) de identidade.

SELECT * INTO TempTable FROM MyTable_T WHERE id = 1;
ALTER TABLE TempTable DROP COLUMN id;
INSERT INTO MyTable_T SELECT * FROM TempTable;
DROP TABLE TempTable;

A coluna "id" é a coluna de identidade e essa é a única coluna que tenho que especificar. É melhor do que o contrário, de qualquer maneira. :-)

Eu uso o SQL Server. Você pode usar " CREATE TABLE" e " UPDATE TABLE" nas linhas 1 e 2. Hmm, vi que realmente não dei a resposta que ele queria. Ele queria copiar o ID para outra coluna também. Mas essa solução é boa para fazer uma cópia com um novo ID automático.

Eu edito minha solução com as idéias de Michael Dibbets.

use MyDatabase; 
SELECT * INTO #TempTable FROM [MyTable] WHERE [IndexField] = :id;
ALTER TABLE #TempTable DROP COLUMN [IndexField]; 
INSERT INTO [MyTable] SELECT * FROM #TempTable; 
DROP TABLE #TempTable;

Você pode soltar mais de uma coluna, separando-as com um ",". O: id deve ser substituído pelo id da linha que você deseja copiar. MyDatabase, MyTable e IndexField devem ser substituídos por seus nomes (é claro).

Jonas
fonte
1
Isso é complicado, como você pode ter certeza de que está copiando o ID correto para a linha correta durante o INSERT INTO na linha 3?
Erno
A coluna id é uma coluna de identidade (na minha tabela), então não me importo com o valor. Eu só quero um novo registro com os mesmos valores que o "antigo".
Jonas
9
Pois TempTable, não seria melhor usar #TempTable, por isso é uma verdadeira tabela temporária?
Jess
3
Estou usando isso no seguinte formato. use MyDatabase; SELECT * INTO #TempTable FROM [TABLE] WHERE [indexfield] = :id; ALTER TABLE #TempTable DROP COLUMN [indexfield]; INSERT INTO [TABLE] SELECT * FROM #TempTable; DROP TABLE #TempTable;Dessa forma, sei que será limpo o mais rápido possível, sem atrasos na gravação de um arquivo intermitente no disco.
Tschallacka 26/03
3
Você também pode usar $identityse você não quer codificar seu nome coluna de identidade
Fator Místico
12

Acho que você está tentando evitar escrever todos os nomes das colunas. Se você estiver usando o SQL Management Studio, poderá clicar com o botão direito do mouse na tabela e em Script como Inserir .. e poderá mexer nessa saída para criar sua consulta.

Matt Hinze
fonte
10

Especifique todos os campos, exceto o seu campo de ID.

INSERT INTO MyTable (FIELD2, FIELD3, ..., FIELD529, PreviousId)
SELECT FIELD2, NULL, ..., FIELD529, FIELD1
FROM MyTable
WHERE FIELD1 = @Id;
Scott Bevington
fonte
6

Tenho o mesmo problema em que quero que um único script funcione com uma tabela que tenha colunas adicionadas periodicamente por outros desenvolvedores. Não apenas isso, mas também estou suportando muitas versões diferentes de nosso banco de dados, pois os clientes podem não estar atualizados com a versão atual.

Peguei a solução por Jonas e a modifiquei um pouco. Isso permite que eu faça uma cópia da linha e altere a chave primária antes de adicioná-la novamente à tabela de origem original. Isso também é realmente útil para trabalhar com tabelas que não permitem valores NULL em colunas e não é necessário especificar o nome de cada coluna no INSERT.

Este código copia a linha de 'ABC' para 'XYZ'

SELECT * INTO #TempRow FROM SourceTable WHERE KeyColumn = 'ABC';
UPDATE #TempRow SET KeyColumn = 'XYZ';
INSERT INTO SourceTable SELECT * FROM #TempRow;
DELETE #TempRow;

Depois de terminar, solte a tabela temporária.

DROP TABLE #TempRow;
TonyT
fonte
4

Eu sei que minha resposta está atrasada para a festa. Mas a maneira que resolvi é um pouco diferente de todas as respostas.

Eu tive uma situação, eu preciso clonar uma linha em uma tabela, exceto algumas colunas. Esses poucos terão novos valores. Esse processo deve oferecer suporte automaticamente para futuras alterações na tabela. Isso implica, clonar o registro sem especificar nenhum nome de coluna.

Minha abordagem é,

  1. Consulte Sys.Columns para obter a lista completa de colunas da tabela e inclua os nomes das colunas a serem ignoradas na cláusula where.
  2. Converta isso em CSV como nomes de colunas.
  3. Compilação Selecionar ... Inserir no script com base nisso.


declare @columnsToCopyValues varchar(max), @query varchar(max)
SET @columnsToCopyValues = ''

--Get all the columns execpt Identity columns and Other columns to be excluded. Say IndentityColumn, Column1, Column2 Select @columnsToCopyValues = @columnsToCopyValues + [name] + ', ' from sys.columns c where c.object_id = OBJECT_ID('YourTableName') and name not in ('IndentityColumn','Column1','Column2') Select @columnsToCopyValues = SUBSTRING(@columnsToCopyValues, 0, LEN(@columnsToCopyValues)) print @columnsToCopyValues

Select @query = CONCAT('insert into YourTableName (',@columnsToCopyValues,', Column1, Column2) select ', @columnsToCopyValues, ',''Value1'',''Value2'',', ' from YourTableName where IndentityColumn =''' , @searchVariable,'''')

print @query exec (@query)

Jeyara
fonte
2
insert into MyTable (uniqueId, column1, column2, referencedUniqueId)
select NewGuid(), // don't know this syntax, sorry
  column1,
  column2,
  uniqueId,
from MyTable where uniqueId = @Id
Jeffrey L Whitledge
fonte
1

Se "key" for o seu campo PK e for numérico automático.

insert into MyTable (field1, field2, field3, parentkey)
select field1, field2, null, key from MyTable where uniqueId = @Id

ele irá gerar um novo registro, copiando o campo1 e o campo2 do registro original

Eduardo Campañó
fonte
1

Minha tabela possui 100 campos e eu precisava de uma consulta para funcionar. Agora posso alternar qualquer número de campos com alguma lógica condicional básica e não me preocupar com sua posição ordinal.

  1. Substitua o nome da tabela abaixo pelo nome da tabela

    SQLcolums = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE (TABLE_NAME = 'TABLE-NAME')"
    
    Set GetColumns = Conn.Execute(SQLcolums)
    Do WHILE not GetColumns.eof
    
    colName = GetColumns("COLUMN_NAME")
  2. Substitua o nome do campo de identidade original pelo seu nome de campo PK

    IF colName = "ORIGINAL-IDENTITY-FIELD-NAME" THEN ' ASSUMING THAT YOUR PRIMARY KEY IS THE FIRST FIELD DONT WORRY ABOUT COMMAS AND SPACES
        columnListSOURCE = colName 
        columnListTARGET = "[PreviousId field name]"
    ELSE
        columnListSOURCE = columnListSOURCE & colName
        columnListTARGET = columnListTARGET & colName
    END IF
    
    GetColumns.movenext
    
    loop
    
    GetColumns.close    
  3. Substitua os nomes das tabelas novamente (nome da tabela de destino e nome da tabela de origem); edite suas wherecondições

    SQL = "INSERT INTO TARGET-TABLE-NAME (" & columnListTARGET & ") SELECT " & columnListSOURCE & " FROM SOURCE-TABLE-NAME WHERE (FIELDNAME = FIELDVALUE)" 
    Conn.Execute(SQL)
Rit Man
fonte
0

Você pode fazer assim:

INSERT INTO DENI/FRIEN01P 
SELECT 
   RCRDID+112,
   PROFESION,
   NAME,
   SURNAME,
   AGE, 
   RCRDTYP, 
   RCRDLCU, 
   RCRDLCT, 
   RCRDLCD 
FROM 
   FRIEN01P      

Em vez de 112, você deve colocar um número do ID máximo na tabela DENI / FRIEN01P.

Denis Kutlubaev
fonte
0

Aqui está como eu fiz isso usando o ASP classic e não consegui fazê-lo funcionar com as respostas acima. Queria poder copiar um produto em nosso sistema para um novo product_id e precisava que ele funcionasse mesmo quando adicione mais colunas à tabela.

Cn.Execute("CREATE TEMPORARY TABLE temprow AS SELECT * FROM product WHERE product_id = '12345'")
Cn.Execute("UPDATE temprow SET product_id = '34567'")
Cn.Execute("INSERT INTO product SELECT * FROM temprow")
Cn.Execute("DELETE temprow")
Cn.Execute("DROP TABLE temprow")
Daniel Nordh
fonte
Eu não acho que você precise emitir 5 comandos SQL separados para concluir isso. E também, quem você determina no segundo comando, que o ID do produto deve ser 34567?
Jeyara
34567 é apenas um exemplo. Você pode configurá-lo para uma variável ou o que quiser. Se você tem uma maneira melhor de fazer isso no ASP Classic, entre em contato. :)
Daniel Nordh
você pode escrever um processo armazenado para executar tudo isso em uma única linha. Além disso, geralmente PK costumava ser um valor de incremento automático. Portanto, atribuir a si mesmo não é uma boa ideia.
Jeyara 15/03/19
Como você escreveria o referido procedimento? Por favor me esclareça. PK? Se você quer dizer IDs de artigo, em nosso sistema, usamos IDs de artigo com base na loja em que os vendemos. Por exemplo, nossa loja de brinquedos possui um número de artigo começando com 30 e nossa loja de esportes começa com 60. Além disso, economizamos espaços para que, se obter uma nova cor, por exemplo, podemos ter um ID do artigo ao lado dos outros do mesmo tipo. Como dito, esta foi a minha solução que funciona em nosso sistema. Como você define variáveis ​​não é tão importante e a maioria dos programadores entenderia o que funciona para eles e seus sistemas.
Daniel Nordh
PK significa chave primária. No seu caso, é '12345'. Consulte stackoverflow.com/questions/21561657/… para saber como usar o proc armazenado no ASP clássico, se você não souber. Para fazer isso, mova todos para um proc armazenado e passe seu '12345' como parâmetro. A maneira como você trabalha, mas os scripts parametrizados são os recomendados atualmente.
Jeyara