¿Cómo se copia un registro en una tabla SQL pero se cambia la identificación única de la nueva fila?

123

Esta pregunta se acerca a lo que necesito, pero mi escenario es ligeramente diferente. La tabla de origen y la tabla de destino son las mismas y la clave primaria es un identificador único (guid). Cuando intento esto:

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

Obviamente obtengo una violación de restricción de clave principal, ya que intento copiar sobre la clave primaria. En realidad, no quiero copiar la clave primaria en absoluto. Más bien, quiero crear uno nuevo. Además, me gustaría copiar selectivamente ciertos campos y dejar los otros nulos. Para hacer las cosas más complejas, necesito tomar la clave primaria del registro original e insertarla en otro campo en la copia (campo Id anterior).

Estoy seguro de que hay una solución fácil para esto, simplemente no conozco suficiente TSQL para saber qué es.

Kilhoffer
fuente

Respuestas:

186

Prueba esto:


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

Cualquier campo no especificado debe recibir su valor predeterminado (que generalmente es NULL cuando no está definido).

AaronSieb
fuente
2
Increíble. Trabajado como un encanto. Era tan obvio una vez que lo escribiste. ¡¡Gracias!!
Kilhoffer
1
Esto funcionará si tiene pocas columnas. Si tiene 20 o 30 columnas, aún puede usar este método, pero sería desalentador. Además, cada vez que agrega una columna y desea copiar esos datos también, debe agregar la columna a este script. Lo mejor es generar insertos dinámicamente usando tablas sys.
Jeyara
93

Ok, sé que es un problema antiguo pero publico mi respuesta de todos modos.

Me gusta esta solución Solo tengo que especificar las columnas de identidad.

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;

La columna "id" es la columna de identidad y esa es la única columna que tengo que especificar. Es mejor que al revés de todos modos. :-)

Yo uso SQL Server. Es posible que desee utilizar " CREATE TABLE" y " UPDATE TABLE" en la fila 1 y 2. Hmm, vi que en realidad no le di la respuesta que él quería. Él quería copiar la identificación a otra columna también. Pero esta solución es buena para hacer una copia con una nueva identificación automática.

Edito mi solución con las ideas 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;

Puede soltar más de una columna separándolas con un ",". El: id debe reemplazarse con el id de la fila que desea copiar. MyDatabase, MyTable e IndexField deben reemplazarse con sus nombres (por supuesto).

Jonas
fuente
1
Esto es complicado, ¿cómo puede estar seguro de que está copiando la identificación correcta en la fila correcta durante INSERTAR EN la línea 3?
Erno
La columna id es una columna de identidad (en mi tabla), por lo que no me importa el valor. Solo quiero un nuevo registro que contenga los mismos valores que el "antiguo".
Jonas
9
Porque TempTable, ¿no sería mejor usarlo #TempTable, por lo que es una verdadera tabla temporal?
Jess
3
Estoy usando esto en el siguiente formato. De 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;esta manera, sé que se limpiará lo antes posible sin demoras al escribir un archivo intermitente en el disco.
Tschallacka
3
También podría usarlo $identitysi no desea codificar el nombre de su columna de identidad
Factor Mystic el
12

Supongo que estás tratando de evitar escribir todos los nombres de columna. Si está usando SQL Management Studio, puede hacer clic con el botón derecho en la tabla y en Script As Insert .. luego puede jugar con esa salida para crear su consulta.

Matt Hinze
fuente
10

Especifique todos los campos excepto su campo ID.

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

Tengo el mismo problema en el que quiero que un solo script funcione con una tabla que tiene columnas agregadas periódicamente por otros desarrolladores. No solo eso, sino que estoy respaldando muchas versiones diferentes de nuestra base de datos, ya que es posible que no todos los clientes estén actualizados con la versión actual.

Tomé la solución de Jonas y la modifiqué un poco. Esto me permite hacer una copia de la fila y luego cambiar la clave principal antes de volver a agregarla a la tabla fuente original. Esto también es muy útil para trabajar con tablas que no permiten valores NULL en columnas y no desea tener que especificar el nombre de cada columna en INSERT.

Este código copia la fila de 'ABC' a 'XYZ'

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

Una vez que haya terminado, suelte la tabla temporal.

DROP TABLE #TempRow;
TonyT
fuente
4

Sé que mi respuesta llega tarde a la fiesta. Pero la forma en que resolví es un poco diferente a todas las respuestas.

Tuve una situación, necesito clonar una fila en una tabla, excepto algunas columnas. Esos pocos tendrán nuevos valores. Este proceso debería ser compatible automáticamente con futuros cambios en la tabla. Esto implica, clonar el registro sin especificar ningún nombre de columna.

Mi enfoque es

  1. Consulte Sys.Columns para obtener la lista completa de columnas de la tabla e incluya los nombres de las columnas para omitir la cláusula where.
  2. Convierta eso a CSV como nombres de columna.
  3. Build Select ... Insertar en script basado en esto.


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

Si "clave" es su campo PK y es autonumérico.

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

generará un nuevo registro, copiando campo1 y campo2 del registro original

Eduardo Campañó
fuente
1

Mi tabla tiene 100 campos, y necesitaba una consulta para simplemente funcionar. Ahora puedo cambiar cualquier número de campos con alguna lógica condicional básica y no preocuparme por su posición ordinal.

  1. Reemplace el nombre de la tabla a continuación con el nombre de su tabla

    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. Reemplace el nombre del campo de identidad original con su nombre 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. Reemplace los nombres de la tabla nuevamente (tanto el nombre de la tabla de destino como el nombre de la tabla de origen); edita tus wherecondiciones

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

Puedes hacer así:

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

Allí, en lugar de 112, debe colocar un número de identificación máxima en la tabla DENI / FRIEN01P.

Denis Kutlubaev
fuente
0

Así es como lo hice usando ASP classic y no pude hacer que funcionara con las respuestas anteriores y quería poder copiar un producto en nuestro sistema a un nuevo product_id y lo necesitaba para poder funcionar incluso cuando agregue más columnas a la tabla.

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
fuente
No creo que necesite emitir 5 comandos SQL separados para completar esto. Y también, ¿a quién determina en el segundo comando, que la identificación del producto debe ser 34567?
Jeyara
34567 es solo un ejemplo. Puede establecerlo en una variable o lo que desee. Si tiene una mejor manera de hacerlo en ASP Classic, hágamelo saber. :)
Daniel Nordh
podría escribir un proceso almacenado para ejecutar todo esto en una sola línea. Además, generalmente PK solía ser un valor de incremento automático. Asignarse a sí mismo no es una buena idea.
Jeyara
¿Cómo escribirías dicho procedimiento? Por favor iluminame. PK? Si te refieres a ID de artículo, en nuestro sistema usamos ID de artículo según la tienda en la que los vendemos. Por ejemplo, nuestra juguetería tiene un número de artículo que comienza con 30 y nuestra tienda de deportes comienza con 60. Además, ahorramos espacios para que si para obtener un nuevo color, por ejemplo, podemos tener una ID de artículo junto a las otras del mismo tipo. Como dije, esta fue mi solución que funciona en nuestro sistema. Cómo establecer variables no es tan importante y la mayoría de los programadores entenderían lo que funciona para ellos y su sistema.
Daniel Nordh
PK significa clave primaria. En su caso es '12345'. Consulte stackoverflow.com/questions/21561657/… para saber cómo usar el proceso almacenado en ASP clásico si no sabe cómo hacerlo. Para hacer esto, mueva todos esos a un proceso almacenado y pase su '12345' como parámetro. La forma en que lo haría funcionaría, pero los scripts parametrizados son los recomendados en estos días.
Jeyara