Come posso aggiornare se esiste, inserire se no (AKA "upsert" o "merge") in MySQL?


152

Esiste un modo semplice per INSERTuna riga quando non esiste o per UPDATEse esiste, utilizzando una query MySQL?

Risposte:


191

Usa INSERT ... ON DUPLICATE KEY UPDATE. Per esempio:

INSERT INTO `usage`
(`thing_id`, `times_used`, `first_time_used`)
VALUES
(4815162342, 1, NOW())
ON DUPLICATE KEY UPDATE
`times_used` = `times_used` + 1

10
Sì, credo che si chiami l'equivalente di SQL Server MERGE. In generale, il concetto viene spesso definito come "UPSERT".
caos,

3
@blub: se crei una chiave univoca attiva gebe topicfunzionerà ( ALTER TABLE table ADD UNIQUE geb_by_topic (geb, topic)).
caos,

1
@Brian: viene chiamato anche l'equivalente di Oracle, MERGEma non sono sicuro che la sua sintassi sia identica a quella di SQL Server.
Ken Keenan,

1
@Brooks: se lo passi a 0, utilizzerà effettivamente 0 come valore. Quindi non farlo. Non passare nulla, o passare a NULL, per consentire al auto_incrementcomportamento di funzionare (che altrimenti, sì, funziona come presumi ; vedi dev.mysql.com/doc/refman/5.5/it/example-auto-increment. html ).
caos,

3
Grazie e puoi usare VALUES(col)per ottenere il valore dall'argomento insert in caso di duplicato. Ad esempio:ON DUPLICATE UPDATE b = VALUES(b), c = VALUES(c)
Gerrytan,

5

So che questa è una vecchia domanda, ma Google mi ha portato qui di recente, quindi immagino che anche altri vengano qui.

@chaos è corretto: c'è la INSERT ... ON DUPLICATE KEY UPDATEsintassi.

Tuttavia, la domanda originale posta su MySQL in particolare, e in MySQL c'è la REPLACE INTO ...sintassi. IMHO, questo comando è più semplice e diretto da utilizzare per gli upsert. Dal manuale:

REPLACE funziona esattamente come INSERT, tranne per il fatto che se una vecchia riga nella tabella ha lo stesso valore di una nuova riga per un PRIMARY KEY o un indice UNIQUE, la vecchia riga viene eliminata prima di inserire la nuova riga.

Nota che questo non è SQL standard. Un esempio dal manuale:

CREATE TABLE test (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  data VARCHAR(64) DEFAULT NULL,
  ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);

mysql> REPLACE INTO test VALUES (1, 'Old', '2014-08-20 18:47:00');
Query OK, 1 row affected (0.04 sec)

mysql> REPLACE INTO test VALUES (1, 'New', '2014-08-20 18:47:42');
Query OK, 2 rows affected (0.04 sec)

mysql> SELECT * FROM test;
+----+------+---------------------+
| id | data | ts                  |
+----+------+---------------------+
|  1 | New  | 2014-08-20 18:47:42 |
+----+------+---------------------+
1 row in set (0.00 sec)

Modifica: solo un giusto avvertimento che REPLACE INTOnon è come UPDATE. Come dice il manuale, REPLACEelimina la riga se esiste, quindi inserisce una nuova. (Nota le divertenti "2 righe interessate" nell'esempio sopra.) Cioè, sostituirà i valori di tutte le colonne di un record esistente (e non aggiornerà semplicemente alcune colonne.) Il comportamento di MySQL REPLACE INTOè molto simile a quello di Sqlite INSERT OR REPLACE INTO. Vedere questa domanda per alcune soluzioni alternative se si desidera aggiornare solo alcune colonne (e non tutte le colonne) se il record esiste già.

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.