DBAtlas · Atlas d'exploitation des bases de données

MERGE / UPSERT (mettre à jour si existe, sinon insérer)

Oracle

MERGE INTO customer m
USING (SELECT 1 AS id, 'Ali' AS name, 500 AS balance FROM dual) k
ON (m.id = k.id)
WHEN MATCHED THEN
  UPDATE SET m.name = k.name, m.balance = k.balance
WHEN NOT MATCHED THEN
  INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);

MERGE (upsert) rapproche une source d'une cible, met à jour les lignes correspondantes et insère les autres ; idéal pour la synchronisation de données en masse. Oracle offre une prise en charge solide de MERGE : WHEN MATCHED ... DELETE et des mises à jour conditionnelles peuvent aussi être écrits. Pour une source d'une seule ligne, on utilise la table dual.

PostgreSQL

-- The PostgreSQL way: INSERT ... ON CONFLICT
INSERT INTO customer (id, name, balance)
VALUES (1, 'Ali', 500)
ON CONFLICT (id) DO UPDATE
  SET name = EXCLUDED.name, balance = EXCLUDED.balance;

L'upsert classique de PostgreSQL est INSERT ... ON CONFLICT (colonne unique) DO UPDATE / DO NOTHING ; les nouvelles valeurs de la ligne en conflit sont accessibles via l'alias EXCLUDED. Depuis PostgreSQL 15, l'instruction MERGE standard est aussi prise en charge. ON CONFLICT est atomique sur une seule table et largement utilisé.

MySQL

INSERT INTO customer (id, name, balance)
VALUES (1, 'Ali', 500)
ON DUPLICATE KEY UPDATE
  name = VALUES(name), balance = VALUES(balance);

MySQL/MariaDB utilisent INSERT ... ON DUPLICATE KEY UPDATE pour les upserts ; en cas de conflit sur une clé unique ou primaire, la ligne est mise à jour. Les nouvelles valeurs sont accessibles avec VALUES(my_column) (ou l'alias de ligne 'new.my_column' en 8.0.19+). INSERT IGNORE ignore silencieusement la ligne en cas de conflit. MySQL n'a pas de MERGE standard.

SQL Server

MERGE INTO customer AS m
USING (VALUES (1, 'Ali', 500)) AS k(id, name, balance)
ON m.id = k.id
WHEN MATCHED THEN
  UPDATE SET m.name = k.name, m.balance = k.balance
WHEN NOT MATCHED THEN
  INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);

SQL Server prend en charge l'instruction MERGE depuis 2008 ; elle offre des branches supplémentaires comme WHEN NOT MATCHED BY SOURCE pour supprimer les lignes excédentaires de la cible. Une instruction MERGE doit se terminer par un point-virgule. Remarque : en raison de certains bugs dans des cas limites, des instructions INSERT/UPDATE séparées peuvent être préférables pour les upserts simples.

MongoDB

// Find-and-update or insert with upsert:true
db.customer.updateOne(
  { id: 1 },
  { $set: { name: "Ali", balance: 500 } },
  { upsert: true }
)

Dans MongoDB, l'upsert est une option native : si { upsert: true } est ajouté à updateOne/updateMany/findOneAndUpdate, un document correspondant est mis à jour, sinon un nouveau document est créé. Pour la synchronisation en masse, l'étape d'agrégation $merge (écriture/mise à jour dans une collection cible) est plus proche du MERGE SQL.

Redis

# SET is a natural upsert: it overwrites the key if present, otherwise creates it.
SET customer:1 '{"name":"Ali","balance":500}'
# Update/add a hash field:
HSET customer:1 name Ali balance 500

Dans Redis, les commandes d'écriture sont par nature des upserts : SET/HSET mettent à jour la clé ou le champ s'il existe, sinon le créent. Utilisez l'option SET ... NX pour écrire uniquement en cas d'absence, et XX uniquement en cas de présence. La distinction « mettre à jour si existe, sinon insérer » est déjà le comportement par défaut de la plupart des commandes.

Elasticsearch

# An index call with a document id acts like an upsert (replaces if present):
PUT customer/_doc/1
{ "name": "Ali", "balance": 500 }
# Partial update + insert if absent:
POST customer/_update/1
{ "doc": { "balance": 500 }, "doc_as_upsert": true }

Dans Elasticsearch, un appel index (PUT) avec le même _id remplace entièrement le document s'il existe, sinon le crée (de type upsert). Pour des mises à jour partielles avec insertion si absent, utilisez 'doc_as_upsert': true ou un bloc 'upsert' dans l'API _update. Les upserts scriptés sont aussi possibles.

Ouvrir dans DBAtlas →