SQL : UNION vs UNION ALL
Combiner les résultats de plusieurs SELECT — avec ou sans doublons, INTERSECT et EXCEPT
🏠 Hub Programmation
🗄️ Cours SQL
🔗 Jointures SQL
🔍 Sous-requêtes
Le principe
UNION combine les résultats de deux (ou plus) requêtes SELECT en un seul jeu de résultats. Contrairement au JOIN qui combine des colonnes (horizontalement), UNION empile des lignes (verticalement).
— clients.nom | commandes.montant
— UNION = empiler des lignes (vertical)
— résultat du SELECT 1
— +
— résultat du SELECT 2
Règle obligatoire : chaque SELECT dans un UNION doit avoir le même nombre de colonnes et des types compatibles. Les noms de colonnes du résultat viennent du premier SELECT.
UNION — sans doublons
UNION supprime automatiquement les lignes en double dans le résultat combiné :
SELECT ville FROM clients
UNION
SELECT ville FROM fournisseurs;
— clients.ville : Paris, Lyon, Paris, Marseille
— fournisseurs.ville : Lyon, Bordeaux, Paris
— Résultat UNION :
— Paris
— Lyon
— Marseille
— Bordeaux
— → 4 lignes (doublons Paris et Lyon supprimés)
Pour supprimer les doublons, UNION effectue un tri implicite (ou un hash) sur toutes les colonnes. C’est une opération coûteuse sur de gros volumes. Si tu n’as pas besoin de dédupliquer, utilise UNION ALL.
UNION ALL — avec doublons
UNION ALL garde toutes les lignes, y compris les doublons :
SELECT ville FROM clients
UNION ALL
SELECT ville FROM fournisseurs;
— Résultat UNION ALL :
— Paris
— Lyon
— Paris
— Marseille
— Lyon
— Bordeaux
— Paris
— → 7 lignes (tout est gardé)
— Combiner les ventes de deux tables (historique + actuel)
SELECT id, client, montant, date FROM ventes_2024
UNION ALL
SELECT id, client, montant, date FROM ventes_2025;
— Enchaîner plus de 2 SELECT
SELECT nom, ‘client’ AS type FROM clients
UNION ALL
SELECT nom, ‘fournisseur’ FROM fournisseurs
UNION ALL
SELECT nom, ’employe’ FROM employes;
Règle de performance : utilise UNION ALL par défaut. N’utilise UNION (sans ALL) que si tu as réellement besoin de supprimer les doublons. UNION ALL est toujours plus rapide car il ne fait aucun tri ni comparaison.
Tableau comparatif
| Critère | UNION | UNION ALL |
|---|---|---|
| Doublons | Supprimés | Conservés |
| Performance | ⚠️ Plus lent (tri/hash pour dédupliquer) | ✅ Plus rapide (simple concaténation) |
| Nombre de lignes | ≤ somme des deux SELECT | = somme des deux SELECT |
| Usage | Quand les doublons posent problème | Cas par défaut (logs, historiques…) |
INTERSECT et EXCEPT
SELECT ville FROM clients
INTERSECT
SELECT ville FROM fournisseurs;
— Paris, Lyon (villes en commun)
— EXCEPT (ou MINUS en Oracle) — lignes du 1er SELECT absentes du 2nd
SELECT ville FROM clients
EXCEPT
SELECT ville FROM fournisseurs;
— Marseille (ville client mais pas fournisseur)
— Clients qui n’ont jamais commandé (alternative à NOT EXISTS)
SELECT id FROM clients
EXCEPT
SELECT client_id FROM commandes;
| Opérateur | Retourne | Équivalent ensembliste |
|---|---|---|
| UNION | Toutes les lignes (sans doublons) | A ∪ B |
| UNION ALL | Toutes les lignes (avec doublons) | Concaténation |
| INTERSECT | Lignes communes | A ∩ B |
| EXCEPT | Lignes du 1er absentes du 2nd | A \ B |
⚠️ INTERSECT et EXCEPT ne sont pas supportés en MySQL avant la version 8.0.31. En MySQL plus ancien, utilise des JOIN ou EXISTS pour obtenir le même résultat.
Cas d’usage courants
SELECT * FROM logs_2025_01
UNION ALL
SELECT * FROM logs_2025_02
UNION ALL
SELECT * FROM logs_2025_03;
SELECT nom, email, ‘client’ AS source FROM clients
UNION ALL
SELECT nom, email, ‘prospect’ FROM prospects
UNION ALL
SELECT nom, email, ‘partenaire’ FROM partenaires
ORDER BY nom; — ORDER BY s’applique au résultat final
— Solution : LEFT JOIN UNION RIGHT JOIN
SELECT c.nom, co.produit
FROM clients c
LEFT JOIN commandes co ON c.id = co.client_id
UNION
SELECT c.nom, co.produit
FROM clients c
RIGHT JOIN commandes co ON c.id = co.client_id;
SELECT nom, montant FROM ventes_2024
UNION ALL
SELECT nom, montant FROM ventes_2025
ORDER BY montant DESC
LIMIT 10;
— Pour trier CHAQUE SELECT individuellement, utilise des sous-requêtes :
(SELECT nom, montant FROM ventes_2024 ORDER BY montant DESC LIMIT 5)
UNION ALL
(SELECT nom, montant FROM ventes_2025 ORDER BY montant DESC LIMIT 5);
Sans parenthèses, ORDER BY et LIMIT s’appliquent au résultat final du UNION. Avec parenthèses autour de chaque SELECT, tu peux trier et limiter chaque partie individuellement.
Erreurs fréquentes
| Erreur | Problème | Solution |
|---|---|---|
| Nombre de colonnes différent | Erreur : each UNION query must have same number of columns | Ajouter des colonnes NULL ou constantes pour aligner |
| Types incompatibles | Conversion implicite inattendue | Utiliser CAST pour homogénéiser |
| UNION au lieu de UNION ALL | Doublons légitimes supprimés + lent | Utiliser UNION ALL par défaut |
| ORDER BY au mauvais endroit | Erreur de syntaxe ou tri sur un seul SELECT | Mettre ORDER BY à la fin du dernier UNION |
| Confondre UNION et JOIN | UNION empile verticalement, JOIN combine horizontalement | JOIN pour lier des tables, UNION pour empiler |
Questions fréquentes
🔗 Jointures SQL
🔍 Sous-requêtes
📊 GROUP BY & HAVING
📑 ORDER BY & LIMIT
🔢 Fonctions d’agrégation
🗄️ Cours SQL complet
🏠 Hub Programmation
UNION vs UNION ALL en SQL — Combiner des résultats
Référence : sql.sh UNION
