Duplica / Copia record nella stessa tabella MySQL


87

Sto cercando da un po 'di tempo ma non riesco a trovare una soluzione facile per il mio problema. Vorrei duplicare un record in una tabella, ma ovviamente la chiave primaria univoca deve essere aggiornata.

Ho questa domanda:

INSERT INTO invoices
    SELECT * FROM invoices AS iv WHERE iv.ID=XXXXX
    ON DUPLICATE KEY UPDATE ID = (SELECT MAX(ID)+1 FROM invoices)

il problema è che questo cambia solo il IDdella riga invece di copiare la riga. Qualcuno sa come risolvere questo problema?

// modifica: vorrei farlo senza digitare tutti i nomi dei campi perché i nomi dei campi possono cambiare nel tempo.

Risposte:


137

Il modo in cui di solito lo faccio è usare una tabella temporanea. Probabilmente non è efficiente dal punto di vista computazionale ma sembra funzionare bene! Qui sto duplicando il record 99 nella sua interezza, creando il record 100.

CREATE TEMPORARY TABLE tmp SELECT * FROM invoices WHERE id = 99;

UPDATE tmp SET id=100 WHERE id = 99;

INSERT INTO invoices SELECT * FROM tmp WHERE id = 100;

Spero che funzioni bene per te!


Mi piace, un passo in mezzo per tenere d'occhio le cose
SeanDowney

4
Se stai copiando un singolo record, potresti eliminare il punto in cui aggiornare e inserire. Quindi puoi semplicemente premere due volte su nella console mysql che visualizzerà la penultima operazione nella cronologia, cambiare l'id nell'aggiornamento che è conveniente alla fine, premere invio, premere su, premere invio di nuovo senza cambiare nulla, quindi ripetere la procedura per più copie. Più veloce.
Cristian Vrabie

4
cosa succede nel caso in cui l'id 100 è già lì. Secondariamente se scegli un nuovo id per la tua nuova riga, e durante l'operazione qualcun altro inserisce una nuova riga e poi il tuo id e l'id appena inserito saranno gli stessi. Questo è il collo di bottiglia in questo codice.
nbhatti2001

6
Puoi usare NULL:CREATE TEMPORARY TABLE tmp SELECT bla FROM invoices WHERE bla; UPDATE tmp SET id=NULL; INSERT INTO invoices SELECT * FROM tmp;
Tobias Beuving

1
@TobiasBeuving Questo è un messaggio di errore incollato perché l'ho NOT NULLimpostato su id. Mi stavo solo chiedendo se avessi suggerimenti per questo caso. È considerata una cattiva pratica?
Marcel

84

La risposta di Alex richiede un po 'di attenzione (ad es. Blocco o transazione) in ambienti multi-client.

Supponendo che il AUTO IDcampo sia il primo nella tabella (un caso normale), possiamo fare uso di transazioni implicite.

    CREA TABELLA TEMPORANEA tmp SELECT * da fatture WHERE ...;
    ALTER TABLE tmp drop ID; # elimina il campo di incremento automatico
    # UPDATE tmp SET ...; # avevo solo bisogno di cambiare altre chiavi univoche
    INSERISCI NELLE fatture SELEZIONA 0, tmp. * FROM tmp;
    DROP TABLE tmp;

Dai documenti MySQL:

Utilizzando AUTO_INCREMENT: è anche possibile assegnare esplicitamente NULL o 0 alla colonna per generare numeri di sequenza.


Questo ha inserito il record con un ID di 0 per me, anche se ho un campo AUTO_INCREMENT. Cambiarlo in null ha funzionato, quindi ti suggerisco di cambiare il codice per usare null invece di 0. Sono su MySQL 5.5.35 (Ubuntu).
Tyler Collier

@TylerCollier: NULLo 0entrambi sono accettabili. Puoi pubblicare screenshot del problema riproducendolo e condividendolo qui.
Ravinder Reddy

Il mio cervello mi ha fatto male cercando di capire la sottoquery neato nella riga 4. Forse questa spiegazione aiuta: la tabella di destinazione invoicescontiene un IDcampo AUTO_INCREMENT . La tabella di origine tmpnon lo fa. Per ottenere la correlazione 1: 1 necessaria durante l'inserimento, specificare a 0come primo campo della selezione che è il campo AUTO_INCREMENT della tabella di destinazione. Il tmp.*seleziona tutti i campi dalla tabella di origine tmp. Perfetto! Ora hai la tua correlazione 1: 1. Risposta fantastica @Tim.
Elbowlobstercowstand

@RavinderReddy NULL e 0 non sono la stessa cosa - è possibile avere un record-id di 0 - usa NULL per questo genere di cose
Tobias Beuving

@TobiasBeuving : lo so bene NULLe 0non sono la stessa cosa. Il contesto attuale è in AUTO_INCREMENTfunzione. Non è possibile inserire 0un NULLvalore o in tali campi. Ma quando li si utilizza come input in tale campo, accanto valore di sequenza verrà assegnato a quel campo di auto maticamente. E da qui il mio commento. Prova su SQL Fiddle: sqlfiddle.com/#!9/d619f/1
Ravinder Reddy

12

Sai per certo che la CHIAVE DUPLICATA si attiverà, quindi puoi selezionare il MAX (ID) +1 in anticipo:

INSERT INTO invoices SELECT MAX(ID)+1, ... other fields ... FROM invoices AS iv WHERE iv.ID=XXXXX 

2
si, ma non voglio digitare tutti gli altri campi (sono più o meno 20, e potrebbero cambiare nel tempo). Non voglio cambiare il codice ogni volta che cambia il tablestructre.
Cifre

Dovrai. È meglio creare uno script che generi l'istruzione SQL da un elenco di campi. È banale.
Ingo

L'unica altra alternativa è usare un trigger di inserimento (se mysql lo supporta).
Ingo

11

Il tuo approccio è buono ma il problema è che usi "*" invece di inserire i nomi dei campi. Se metti tutti i nomi delle colonne tranne la chiave primaria, il tuo script funzionerà come un fascino su uno o più record.

INSERT INTO invoices (iv.field_name, iv.field_name,iv.field_name ) SELECT iv.field_name, iv.field_name,iv.field_name FROM invoices AS iv WHERE iv.ID=XXXXX


Tuttavia, ciò significa che dovrai modificare il codice non appena un campo viene aggiunto o eliminato dalla tabella, cosa che personalmente trovo un MAJOR no-no. (Non sempre, ma abbastanza spesso.)
Roemer

10

Una risposta tardiva lo so, ma è ancora una domanda comune, vorrei aggiungere un'altra risposta che ha funzionato per me, utilizzando solo una singola insert intoistruzione di riga , e penso che sia semplice, senza creare alcuna nuova tabella (poiché potrebbe essere un problema con le CREATE TEMPORARY TABLEautorizzazioni):

INSERT INTO invoices (col_1, col_2, col_3, ... etc)
  SELECT
    t.col_1,
    t.col_2,
    t.col_3,
    ...
    t.updated_date,
  FROM invoices t;

La soluzione funziona per la AUTO_INCREMENTcolonna id, altrimenti puoi aggiungere anche la IDcolonna all'istruzione:

INSERT INTO invoices (ID, col_1, col_2, col_3, ... etc)
  SELECT
    MAX(ID)+1,
    t.col_1,
    t.col_2,
    t.col_3,
    ... etc ,
  FROM invoices t;

È davvero facile e diretto, puoi aggiornare qualsiasi altra cosa in una singola riga senza alcuna seconda istruzione di aggiornamento per dopo, (es: aggiornare una colonna del titolo con testo extra o sostituire una stringa con un'altra), inoltre puoi essere specifico con cosa esattamente si desidera duplicare, se tutto allora è, se alcuni, è possibile farlo.


2
Questa è anche una buona base per modificare alcuni campi durante la copia. Ho solo usato INSERT into wp_options SELECT NULL, 'theme_mods_twopress', option_value, autoload FROM wp_options where option_name = 'theme_mods_onepress';per copiare aumentando l'ingresso ( AUTO_INCREMENT NULLtrucco dall'alto) e anche il option_namecampo.
Marcel Waldvogel

Sì .. Buona idea per me. Grazie!!
Ragu Natarajan,

La soluzione migliore
Erlon Charles il

3

Volevo solo estendere l'ottima risposta di Alex per renderla appropriata se ti capita di voler duplicare un intero set di record:

SET @x=7;
CREATE TEMPORARY TABLE tmp SELECT * FROM invoices;
UPDATE tmp SET id=id+@x;
INSERT INTO invoices SELECT * FROM tmp;

Dovevo solo farlo e ho trovato la risposta di Alex un perfetto punto di partenza !. Ovviamente, devi impostare @x sul numero di riga più alto nella tabella (sono sicuro che potresti prenderlo con una query). Questo è utile solo in questa situazione molto specifica, quindi fai attenzione a usarlo quando non desideri duplicare tutte le righe. Modifica la matematica secondo necessità.


2

Ho un problema simile e questo è quello che sto facendo:

insert into Preguntas  (`EncuestaID`, `Tipo` , `Seccion` , `RespuestaID` , `Texto` )  select '23', `Tipo`, `Seccion`, `RespuestaID`, `Texto` from Preguntas where `EncuestaID`= 18

Stato Preguntas:

CREATE TABLE IF NOT EXISTS `Preguntas` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `EncuestaID` int(11) DEFAULT NULL,
  `Tipo` char(5) COLLATE utf8_unicode_ci DEFAULT NULL,
  `Seccion` int(11) DEFAULT NULL,
  `RespuestaID` bigint(11) DEFAULT NULL,
  `Texto` text COLLATE utf8_unicode_ci ,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=522 ;

Quindi, IDviene automaticamente incrementato e sto anche usando un valore fisso ('23') per EncuestaID.


scusa, mi sono appena reso conto che non vuoi usare il nome delle colonne, quindi questo non si applica
Alex Angelico

1

Avevo bisogno anche di questo; la mia soluzione era usare SQLYOG (versione gratuita) per esportare il record desiderato come SQL (crea un inserimento).

Ho quindi modificato manualmente questo per rimuovere l'id in quanto deve essere generato automaticamente e quindi copiato l'inserto in SQLYog per eseguirlo. Questo è stato indolore. Immagino che anche molte altre GUI MySQL possano farlo.

Questo mi fornisce un record che posso utilizzare a scopo di test su un sistema live.

Ora ho anche questo inserto per il riutilizzo, poiché la tabella viene riscritta quotidianamente.


0

Leggera variazione, la differenza principale consiste nell'impostare il campo della chiave primaria ("varname") su null, che produce un avviso ma funziona. Impostando la chiave primaria su null, l'incremento automatico funziona quando si inserisce il record nell'ultima istruzione.

Questo codice elimina anche i tentativi precedenti e può essere eseguito più di una volta senza problemi:

DELETE FROM `tbl` WHERE varname="primary key value for new record";
DROP TABLE tmp;
CREATE TEMPORARY TABLE tmp SELECT * FROM `tbl` WHERE varname="primary key value for old record";
UPDATE tmp SET varname=NULL;
INSERT INTO `tbl` SELECT * FROM tmp;

0

È possibile modificare la tabella temporaneamente per modificare il campo ID in un bigint o giù di lì senza il requisito NOT NULL, quindi impostare l'ID su 0 in quella tabella temporanea. Dopodiché aggiungilo di nuovo alla tabella originale e il NULL attiverà l'incremento automatico.

CREATE TEMPORARY TABLE tmptable SELECT * FROM x WHERE (id='123');
ALTER TABLE tmptable CHANGE id id bigint;
UPDATE tmptable SET id = NULL;
INSERT INTO x SELECT * FROM tmptable;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.