Comment mettre à jour s'il existe, insérer sinon (AKA «upsert» ou «merge») dans MySQL?

152

Existe-t-il un moyen facile d'accéder à INSERTune ligne lorsqu'elle n'existe pas, ou UPDATEsi elle existe, en utilisant une requête MySQL?

blub
la source

Réponses:

191

Utilisez INSERT ... ON DUPLICATE KEY UPDATE. Par exemple:

INSERT INTO `usage`
(`thing_id`, `times_used`, `first_time_used`)
VALUES
(4815162342, 1, NOW())
ON DUPLICATE KEY UPDATE
`times_used` = `times_used` + 1
le chaos
la source
10
Ouais, je crois que l'équivalent de SQL Server est appelé MERGE. En général, le concept est souvent appelé "UPSERT".
chaos
3
@blub: Si vous créez une clé unique sur gebet topiccela fonctionnera ( ALTER TABLE table ADD UNIQUE geb_by_topic (geb, topic)).
chaos
1
@Brian: l'équivalent d'Oracle est également appelé MERGEmais je ne suis pas sûr que sa syntaxe soit identique à celle de SQL Server.
Ken Keenan
1
@Brooks: Si vous lui passez un 0, il utilisera en fait 0 comme valeur. Alors ne fais pas ça. Ne lui transmettez rien, ou ne le transmettez pas a NULL, pour permettre au auto_incrementcomportement de fonctionner (qui sinon, oui, fonctionne comme vous le supposez; voir dev.mysql.com/doc/refman/5.5/en/example-auto-increment. html ).
chaos
3
Merci, et vous pouvez utiliser VALUES(col)pour obtenir la valeur de l'argument d'insertion en cas de duplication. Ex:ON DUPLICATE UPDATE b = VALUES(b), c = VALUES(c)
gerrytan
5

Je sais que c'est une vieille question, mais Google m'a conduit ici récemment, alors j'imagine que d'autres viennent ici aussi.

@chaos est correct: il y a la INSERT ... ON DUPLICATE KEY UPDATEsyntaxe.

Cependant, la question originale posée spécifiquement sur MySQL, et dans MySQL, il y a la REPLACE INTO ...syntaxe. À mon humble avis, cette commande est plus facile et plus simple à utiliser pour les upserts. À partir du manuel:

REPLACE fonctionne exactement comme INSERT, sauf que si une ancienne ligne de la table a la même valeur qu'une nouvelle ligne pour une PRIMARY KEY ou un index UNIQUE, l'ancienne ligne est supprimée avant l'insertion de la nouvelle ligne.

Notez que ce n'est pas du SQL standard. Un exemple tiré du manuel:

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)

Edit: Juste un avertissement qui REPLACE INTOne ressemble pas UPDATE. Comme le dit le manuel, REPLACEsupprime la ligne si elle existe, puis en insère une nouvelle. (Notez le drôle "2 lignes affectées" dans l'exemple ci-dessus.) Autrement dit, cela remplacera les valeurs de toutes les colonnes d'un enregistrement existant (et ne mettra pas simplement à jour certaines colonnes.) Le comportement de MySQL REPLACE INTOressemble beaucoup à celui de Sqlite INSERT OR REPLACE INTO. Consultez cette question pour obtenir des solutions de contournement si vous ne souhaitez mettre à jour que quelques colonnes (et non toutes les colonnes) si l'enregistrement existe déjà.

Tatou Jim
la source