MERGE (UPSERT) – INSERT ou UPDATE (Standard SQL)
Bonne lecture et bon apprentissage !
Junior TSAFACK – 08/08/2026
⏱️ Temps de lecture estimé : 5 minutes
Dans le langage SQL, la commande MERGE permet d’insérer ou de mettre à jour des données dans une table. Cette commande permet d’éviter d’effectuer plusieurs requêtes pour savoir si une donnée est déjà dans la base de données et ainsi adapter l’utilisation d’une requête pour ajouter ou une autre pour modifier la donnée existante. Cette commande peut aussi s’appeler « upset » (contraction de UPDATE + INSERT).
Bon à savoir : Bien que l’instruction ait été ajoutée dans le standard SQL:2003, les différents SGBD n’utilisent pas toutes les mêmes méthodes pour effectuer un UPSERT.
Syntaxe standard
Section titled “Syntaxe standard”La syntaxe standard pour effectuer un MERGE consiste à utiliser une requête SQL semblable à celle ci-dessous :
MERGE INTO table1USING table_referenceON (conditions)WHEN MATCHED THEN UPDATE SET table1.colonne1 = valeur1, table1.colonne2 = valeur2 DELETE WHERE conditions2WHEN NOT MATCHED THEN INSERT (colonne1, colonne3) VALUES (valeur1, valeur3);Explications détaillées :
MERGE INTOpermet de sélectionner la table à modifier.USINGetONpermettent de lister les données sources et la condition de correspondance.WHEN MATCHEDpermet de définir la condition de mise à jour lorsque la condition est vérifiée.WHEN NOT MATCHEDpermet de définir la condition d’insertion lorsque la condition n’est pas vérifiée.
Exemple concret
Section titled “Exemple concret”Reprenons l’exemple du système de votes du chapitre précédent avec la table « vote » :
Table « vote » :
| id | produit_id | vote_count | vote_first_date | vote_last_date |
|---|---|---|---|---|
| 1 | 46 | 2 | 2012-04-25 17:45:24 | 2013-02-16 09:47:02 |
| 2 | 39 | 4 | 2012-04-28 16:54:44 | 2013-02-14 21:04:35 |
| 3 | 49 | 1 | 2012-04-25 19:11:09 | 2013-01-06 20:32:57 |
Avec MERGE, nous pouvons insérer un nouveau vote ou mettre à jour un vote existant en une seule requête :
MERGE INTO vote vUSING (SELECT 50 AS produit_id, 1 AS vote_count, SYSDATE AS vote_date FROM dual) srcON (v.produit_id = src.produit_id)WHEN MATCHED THEN UPDATE SET v.vote_count = v.vote_count + src.vote_count, v.vote_last_date = src.vote_dateWHEN NOT MATCHED THEN INSERT (produit_id, vote_count, vote_first_date, vote_last_date) VALUES (src.produit_id, src.vote_count, src.vote_date, src.vote_date);Explication :
USINGdéfinit une source de données temporaire contenant le produit_id, le nombre de votes et la date.ONspécifie la condition de correspondance : on cherche si le produit existe déjà dans la tablevote.- Si le produit existe (
WHEN MATCHED), on met à jour le compteur et la date du dernier vote. - Si le produit n’existe pas (
WHEN NOT MATCHED), on insère une nouvelle ligne.
Compatibilité selon les SGBD
Section titled “Compatibilité selon les SGBD”Les systèmes de gestion de bases de données peuvent implémenter cette fonctionnalité soit de façon standard, en utilisant une commande synonyme ou en utilisant une syntaxe non standard.
| SGBD | Support et syntaxe |
|---|---|
| SQL Server | MERGE (syntaxe standard) |
| Oracle | MERGE (syntaxe standard, avec FROM dual pour la source) |
| DB2 | MERGE (syntaxe standard) |
| Teradata | MERGE (syntaxe standard) |
| EXASOL | MERGE (syntaxe standard) |
| PostgreSQL | INSERT ... ON CONFLICT DO UPDATE (non standard) |
| MySQL | INSERT ... ON DUPLICATE KEY UPDATE (non standard) |
| SQLite | INSERT ... ON CONFLICT DO UPDATE (depuis 3.24.0) |
Exemples alternatifs par SGBD
Section titled “Exemples alternatifs par SGBD”PostgreSQL (ON CONFLICT)
Section titled “PostgreSQL (ON CONFLICT)”INSERT INTO vote (produit_id, vote_count, vote_first_date, vote_last_date)VALUES (50, 1, NOW(), NOW())ON CONFLICT (produit_id)DO UPDATE SET vote_count = vote_count + 1, vote_last_date = NOW();SQL Server et Oracle (MERGE)
Section titled “SQL Server et Oracle (MERGE)”SQL Server :
MERGE INTO vote AS vUSING (VALUES (50, 1, GETDATE())) AS src (produit_id, vote_count, vote_date)ON v.produit_id = src.produit_idWHEN MATCHED THEN UPDATE SET v.vote_count = v.vote_count + src.vote_count, v.vote_last_date = src.vote_dateWHEN NOT MATCHED THEN INSERT (produit_id, vote_count, vote_first_date, vote_last_date) VALUES (src.produit_id, src.vote_count, src.vote_date, src.vote_date);Oracle :
MERGE INTO vote vUSING (SELECT 50 AS produit_id, 1 AS vote_count, SYSDATE AS vote_date FROM dual) srcON (v.produit_id = src.produit_id)WHEN MATCHED THEN UPDATE SET v.vote_count = v.vote_count + src.vote_count, v.vote_last_date = src.vote_dateWHEN NOT MATCHED THEN INSERT (produit_id, vote_count, vote_first_date, vote_last_date) VALUES (src.produit_id, src.vote_count, src.vote_date, src.vote_date);SQLite (ON CONFLICT)
Section titled “SQLite (ON CONFLICT)”INSERT INTO vote (produit_id, vote_count, vote_first_date, vote_last_date)VALUES (50, 1, datetime('now'), datetime('now'))ON CONFLICT (produit_id)DO UPDATE SET vote_count = vote_count + 1, vote_last_date = datetime('now');Utilisation avancée de MERGE
Section titled “Utilisation avancée de MERGE”1. Mise à jour conditionnelle avec DELETE
Section titled “1. Mise à jour conditionnelle avec DELETE”Il est possible de supprimer une ligne lors d’une correspondance si une condition supplémentaire est remplie :
MERGE INTO vote vUSING (SELECT 50 AS produit_id, 0 AS vote_count FROM dual) srcON (v.produit_id = src.produit_id)WHEN MATCHED THEN UPDATE SET v.vote_count = v.vote_count + src.vote_count DELETE WHERE v.vote_count <= 0;2. MERGE avec une table source multiple
Section titled “2. MERGE avec une table source multiple”La source peut être une table réelle, une vue ou une sous-requête complexe :
MERGE INTO produit pUSING ( SELECT c.produit_id, SUM(c.quantite) AS total_vendu FROM commande c GROUP BY c.produit_id) srcON (p.id = src.produit_id)WHEN MATCHED THEN UPDATE SET p.stock = p.stock - src.total_vendu;Comparaison des méthodes UPSERT
Section titled “Comparaison des méthodes UPSERT”| Méthode | Standard SQL | Portabilité | Performance |
|---|---|---|---|
MERGE (standard) |
Oui | Moyenne (selon SGBD) | Bonne |
INSERT ... ON DUPLICATE KEY UPDATE |
Non (MySQL) | Faible | Très bonne |
INSERT ... ON CONFLICT DO UPDATE |
Non (PostgreSQL) | Faible | Très bonne |
| SELECT + INSERT/UPDATE (2 requêtes) | Oui | Excellente | Mauvaise (2 appels) |
Bonnes pratiques
Section titled “Bonnes pratiques”-
Utilisez la syntaxe la plus adaptée à votre SGBD : Si vous utilisez PostgreSQL, préférez
ON CONFLICT. Si vous utilisez Oracle ou SQL Server, utilisezMERGE. -
Testez les performances : Sur de très grandes tables, le
MERGEpeut être plus lent qu’une approche en deux étapes (d’abord unUPDATE, puis unINSERTdes lignes non modifiées). Testez selon votre contexte. -
Faites attention aux transactions :
MERGEest une opération atomique, mais il est recommandé de l’utiliser dans une transaction pour garantir la cohérence. -
Évitez les conditions complexes dans
ON: L’utilisation d’expressions complexes peut dégrader les performances. Préférez des conditions simples sur des colonnes indexées. -
Préférez une solution portable si votre application doit fonctionner sur plusieurs SGBD : dans ce cas, utilisez une approche avec
SELECTpuisINSERT/UPDATEgérée par le code applicatif.
Prochain chapitre
Section titled “Prochain chapitre”Vous savez désormais comment effectuer un UPSERT avec MERGE. Mais que faire si vous souhaitez vider complètement une table tout en conservant sa structure, et ce de manière très rapide ?
👉 Chapitre 25 : TRUNCATE TABLE – Vider une table
Bonne continuation !
Junior TSAFACK – 08/08/2026