Aggiornamento chiave duplicata uguale a Inserisci


158

Ho cercato in giro ma non ho trovato se è possibile.

Ho questa query MySQL:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

L'ID campo ha un "indice univoco", quindi non possono essercene due. Ora, se lo stesso ID è già presente nel database, vorrei aggiornarlo. Ma devo davvero specificare di nuovo tutti questi campi, come:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

O:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Ho già specificato tutto nell'inserto ...

Una nota in più, mi piacerebbe usare il lavoro in giro per ottenere l'ID!

id=LAST_INSERT_ID(id)

Spero che qualcuno possa dirmi qual è il modo più efficiente.


9
AFAIK, il metodo per riformulare ogni colonna, a=VALUES(a), b=VALUES(b), ...è il modo in cui devi procedere. È così che lo faccio per tutte le mie INSERT ON DUPLICATE KEY UPDATEdichiarazioni.
Valdogg21,

4
Giusto per chiarire, tutto quanto indicato qui è corretto fintanto che si capisce che la domanda a cui si risponde è: È necessario riaffermare ogni colonna che VUOI AGGIORNARE? Sì, se si desidera inserire o aggiornare 8 colonne in una tabella a 25 colonne, è necessario indicare le 8 colonne due volte: una volta nella parte Inserisci e una volta nella parte Aggiornamento. (È possibile saltare la chiave primaria nella parte di aggiornamento, ma è più semplice preformattare una stringa "a = 1, b = 2, c = 3" e incorporarla due volte.) Tuttavia, NON è necessario riaffermare le restanti 17 colonne non vuoi cambiare. Se diventa un AGGIORNAMENTO, manterranno i loro valori esistenti.
Timberline,

3
Ho aggiunto quel commento perché le domande e le risposte non mi lasciavano incerto sulle colonne non menzionate con questo tipo di INSERT, che ho poi testato dopo aver appreso esattamente cosa testare. INSERT ON DUPLICATE KEY UPDATE lascia invariate le colonne non menzionate su UPDATE ma fornisce loro i valori predefiniti su INSERT. Con REPLACE INTO, le colonne non menzionate ottengono sempre valori predefiniti, mai valori esistenti.
Timberline,

1
Controlla stackoverflow.com/questions/2472229/… , penso che ci sia quello che stai cercando
Chococroc

Risposte:


82

L' UPDATEistruzione viene fornita in modo che i campi più vecchi possano essere aggiornati al nuovo valore. Se i tuoi valori precedenti sono gli stessi di quelli nuovi, perché dovresti aggiornarli in ogni caso?

Per es. se le colonne adi gsono già impostati come 2a 8; non sarebbe necessario aggiornarlo nuovamente.

In alternativa, puoi usare:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Per ottenere il idda LAST_INSERT_ID; devi specificare l'app di back-end che stai utilizzando per la stessa.

Per LuaSQL, a conn:getlastautoid()recupera il valore.


6
È solo per l'esempio. In una query di vita reale ci sono differenze. La mia domanda è semplice: devo davvero specificare tutti i campi (e i valori nel mio primo esempio) se sono gli stessi dell'inserto? Voglio solo inserire tutto o se esiste una corrispondenza di valore univoca: aggiorna tutto.
Roy,

1
"Una nota in più, mi piacerebbe usare il lavoro in giro per ottenere anche l'ID!"
Agamemnus,

1
@Tresdin, Per quanto ne so, è solo zucchero sintattico. Personalmente, non ho mai visto nessuno usare a = VALUES (a), b = VALUES (b), ecc. Forse puoi assegnare più valori alla colonna? Non sono sicuro del perché non dovresti semplicemente concatenare i dati in anticipo però.
DuckPuncher,

54
Se la tabella tblha già la riga (1,10), allora: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)imposterà a = 2 . Mentre INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=aimposterà a = 10
Bùi Việt Thành

2
Perché tutti i voti positivi? Questo non funziona. Come ha sottolineato @ Bùi Việt Thành, utilizzando gli a=aaggiornamenti nulla. (Le query nella domanda originale in realtà non aggiornano nulla, ma un aggiornamento sembra essere quello che l'OP intendeva.)
Roger Dueck,

41

Esiste un'estensione specifica di MySQL a SQL che potrebbe essere ciò che desideri: REPLACE INTO

Tuttavia, non funziona esattamente come "ON DUPLICATE UPDATE"

  1. Elimina la vecchia riga che si scontra con la nuova riga e quindi inserisce la nuova riga. Finché non hai una chiave primaria sulla tabella andrebbe bene, ma se lo fai, quindi se qualsiasi altra tabella fa riferimento a quella chiave primaria

  2. Non puoi fare riferimento ai valori nelle vecchie righe, quindi non puoi fare un equivalente di

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

Vorrei usare il lavoro per ottenere l'ID!

Dovrebbe funzionare: last_insert_id () dovrebbe avere il valore corretto fintanto che la chiave primaria si auto-incrementa.

Tuttavia, come ho detto, se in realtà usi quella chiave primaria in altre tabelle, REPLACE INTOprobabilmente non sarà accettabile per te, poiché cancella la vecchia riga che si è scontrata tramite la chiave univoca.

Qualcun altro ha suggerito prima di poter ridurre la digitazione facendo:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);

Quindi, non riesco a rimanere con la chiave primaria prima di una replace intoquery?
Roy,

No: quella riga viene eliminata e viene creata una nuova riga, che avrà una nuova chiave primaria.
Danack,

questo non rallenterebbe il processo su grandi set di dati e come l'importazione di CSV con 100K o più righe?
Muhammad Omer Aslam,

24

Non c'è altro modo, devo specificare tutto due volte. Primo per l'inserimento, secondo nel caso di aggiornamento.


9
Questo non è corretto e non dovrebbe essere la risposta accettata. La risposta di @Danack è la risposta corretta.
Wayne Workman,

2
Dovresti rileggere la domanda @WayneWorkman, questa è la risposta corretta.
Roy,

24

Ecco una soluzione al tuo problema:

Ho cercato di risolvere un problema come il tuo e voglio suggerire di provare da un aspetto semplice.

Segui questi passaggi: Impara da una soluzione semplice.

Passaggio 1: creare uno schema di tabella utilizzando questa query SQL:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Una tabella <code> user </code> con dati

Passaggio 2: creare un indice di due colonne per impedire dati duplicati utilizzando la seguente query SQL:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

oppure, Creare un indice di due colonne dalla GUI come segue: Crea indice dalla GUI Seleziona le colonne per creare l'indice Indici della tabella <code> user </code>

Passaggio 3: aggiorna se esiste, inserisci se non usi le seguenti query:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Tabella <code> user </code> dopo l'esecuzione sopra la query


1
Grazie per questa procedura dettagliata - mi ha fatto capire, molto apprezzato, grazie!
Michael Nilsson,

La FYI che memorizza le password in testo semplice è un'idea terribile, così come sta cercando di prevenire password duplicate a livello di database.
OrangeDog,

10

Nel caso in cui sia possibile utilizzare un linguaggio di scripting per preparare le query SQL, è possibile riutilizzare le coppie field = value utilizzando SETinvece di (a,b,c) VALUES(a,b,c).

Un esempio con PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Tabella di esempio:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

4
Dovresti sfuggire ai tuoi valori o eseguire un'istruzione preparata con i tuoi valori.
Chinoto Vokro,

1
@ChinotoVokro - Sono d'accordo. Questo sta solo presentando un "come potresti" e non sta nemmeno usando alcuna funzione di query nell'esempio.
Chris,

1
Questa è una buona alternativa alla specifica di una matrice di chiavi e una matrice di valori. Grazie.
Mark Carpenter Jr,

5

Potresti voler considerare l'uso della REPLACE INTOsintassi, ma ti avverto, dopo aver duplicato la chiave PRIMARY / UNIQUE, ELIMINA la riga e INSERTI uno nuovo.

Non sarà necessario specificare nuovamente tutti i campi. Tuttavia, è necessario considerare la possibile riduzione delle prestazioni (dipende dalla progettazione della tabella).

Avvertenze:

  • Se hai la chiave primaria AUTO_INCREMENT, ne verrà assegnata una nuova
  • Gli indici dovranno probabilmente essere aggiornati

c'è una violazione del vincolo se l'indice è anche una chiave esterna per un'altra tabella, è attivato dalla parte di eliminazione.
user3290180,

4

So che è tardi, ma spero che qualcuno sarà aiutato da questa risposta

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Puoi leggere il tutorial qui sotto:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/


Buona risposta. Ma l'uso di VALUES () per fare riferimento alle nuove righe è obsoleto a partire da MySQL 8.0.20 . Il nuovo approccio, dettagliato nel collegamento, è utilizzare un alias per la riga. In questo esempio, l'alias è new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473

@ user697473 Ricevo l'errore: "Si è verificato un errore nella sintassi SQL; controllare il manuale corrispondente alla versione del server MariaDB per la sintassi corretta da utilizzare vicino a 'COME nuovo IN AGGIORNAMENTO CHIAVE DUPLICATO a = nuovo.a'" e la mia query funzionava (tranne quando si sono verificate chiavi duplicate, ovviamente) prima di implementare questa clausola. Qualche idea?
Aaron,

@aaron - scusa, nessuna grande idea. MySQL 8.0.20 è stato appena rilasciato alla fine di aprile; forse MariaDB non ha ancora raggiunto. Questo potrebbe spiegare perché ti sta dando un errore.
user697473

@ user697473 Sì, in realtà ho appena provato il metodo a = VALUES (a) nella risposta originale e ha funzionato, grazie comunque.
Aaron

-7

puoi usare insert ignore per questo caso, ignorerà se ottiene record duplicati INSERT IGNORE ...; - senza ON DUPLICATE KEY


Volevo inserirlo quando non presente, altrimenti aggiornarlo. Non ignorarlo.
Roy

perché vuoi aggiornarlo se stai aggiornando con lo stesso valore / precedente, se è un nuovo valore allora va bene con esso, se non inserisci ignora il lavoro per esso
pitu

2
Molto probabilmente perché i valori possono cambiare e vuole creare un record se non esiste, ma aggiornare un record esistente se lo fa (con modifiche) senza l'overhead di un round trip di nuovo all'applicazione e quindi di nuovo al database ancora.
mopsyd,
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.