Les JOINs sont le fondement de SQL. Si vous ne maîtrisez qu'un seul concept pour votre entretien technique, ce doit être celui-ci. Dans tous les entretiens data analyst, data engineer ou data scientist, les questions sur les JOINs arrivent en premier — et elles éliminent beaucoup de candidats.
Thomas Leroy vous propose ici un guide complet avec des exemples concrets, des schémas clairs et des exercices corrigés pour que le sujet soit verrouillé le jour de l'entretien.
📌 Ce qu'il faut retenir
- INNER JOIN ne retourne que les lignes avec correspondances dans les deux tables
- LEFT JOIN conserve toutes les lignes de gauche, même sans correspondance
- RIGHT JOIN conserve toutes les lignes de droite (rarement utilisé)
- FULL OUTER JOIN combine toutes les lignes des deux tables
- Le piège WHERE + LEFT JOIN transforme silencieusement un LEFT JOIN en INNER JOIN
- Toujours vérifier les NULL et les valeurs orphelines avant de joindre
Les tables d'exemple
Pour tous les exemples de cet article, nous utiliserons deux tables :
Table employes :
| id | nom | departement_id |
|---|---|---|
| 1 | Alice | 10 |
| 2 | Bob | 20 |
| 3 | Charlie | 30 |
| 4 | Diana | NULL |
Table departements :
| id | nom_departement |
|---|---|
| 10 | Marketing |
| 20 | Tech |
| 40 | Finance |
Remarquez les cas spéciaux : Charlie a un departement_id = 30 qui n'existe pas dans departements. Diana a un departement_id NULL. Le département Finance (id 40) n'a aucun employé. Ces cas sont précisément ceux que les recruteurs testent.
INNER JOIN : l'intersection
INNER JOIN renvoie uniquement les lignes qui ont une correspondance dans les deux tables.
SELECT
e.nom,
d.nom_departement
FROM employes e
INNER JOIN departements d ON e.departement_id = d.id;
<div style="overflow-x:auto;margin:20px 0;"><table>
<thead style="background-color:#0F172A;color:white;">
<tr><th style="padding:12px 16px;text-align:left">nom</th><th style="padding:12px 16px;text-align:left">nom_departement</th></tr>
</thead><tbody>
<tr style="background:#f8fafc"><td style="padding:12px 16px">Alice</td><td style="padding:12px 16px">Marketing</td></tr>
<tr><td style="padding:12px 16px">Bob</td><td style="padding:12px 16px">Tech</td></tr>
</tbody></table></div>
Seuls Alice et Bob apparaissent : ce sont les seuls employés dont le `departement_id` correspond à un `id` dans `departements`.
- Charlie (departement_id = 30) : pas de correspondance → exclu
- Diana (departement_id = NULL) : pas de correspondance → exclue
- Finance (id = 40) : aucun employé → exclu
### Quand utiliser INNER JOIN
Utilisez INNER JOIN quand vous voulez **uniquement les lignes qui matchent des deux côtés**. C'est le JOIN le plus courant et le plus performant.
### Exemple concret : rapport de ventes par région
```sql
-- Seulement les vendeurs assignés à une région avec leurs chiffres
SELECT
v.nom_vendeur,
r.nom_region,
SUM(v.montant_vente) as total_ventes
FROM vendeurs v
INNER JOIN regions r ON v.region_id = r.id
WHERE v.date_vente >= '2024-01-01'
GROUP BY v.nom_vendeur, r.nom_region;
Cette requête exclut automatiquement les vendeurs sans région assignée et les régions sans vendeur actif.
## LEFT JOIN : toutes les lignes de gauche
`LEFT JOIN` (ou `LEFT OUTER JOIN`) renvoie **toutes les lignes de la table de gauche**, avec les données de la table de droite quand elles existent. Quand il n'y a pas de correspondance, les colonnes de droite sont remplies avec NULL.
```sql
SELECT
e.nom,
d.nom_departement
FROM employes e
LEFT JOIN departements d ON e.departement_id = d.id;
| nom | nom_departement |
|---|---|
| Alice | Marketing |
| Bob | Tech |
| Charlie | NULL |
| Diana | NULL |
Tous les employés apparaissent. Charlie et Diana n'ont pas de correspondance, donc nom_departement est NULL.
⚠️ Attention
Ajouter une condition WHERE sur une colonne de la table de droite annule l'effet du LEFT JOIN et le transforme silencieusement en INNER JOIN. Les lignes où la colonne de droite est NULL (c'est-à-dire les lignes sans correspondance) sont éliminées par le filtre. Ce piège revient dans la quasi-totalité des entretiens SQL.
Le piège du WHERE avec LEFT JOIN
Un piège classique en entretien :
-- PIÈGE : ce LEFT JOIN se comporte comme un INNER JOIN
SELECT e.nom, d.nom_departement
FROM employes e
LEFT JOIN departements d ON e.departement_id = d.id
WHERE d.nom_departement = 'Marketing';
Le WHERE filtre après le JOIN, donc il élimine les lignes où nom_departement est NULL. Pour filtrer sur la table de droite tout en conservant le LEFT JOIN, utilisez la condition dans le ON :
-- Correct : filtre dans le ON
SELECT e.nom, d.nom_departement
FROM employes e
LEFT JOIN departements d
ON e.departement_id = d.id
AND d.nom_departement = 'Marketing';
| nom | nom_departement |
|---|---|
| Alice | Marketing |
| Bob | NULL |
| Charlie | NULL |
| Diana | NULL |
Trouver les lignes sans correspondance
Un cas d'usage majeur du LEFT JOIN : identifier les lignes orphelines. Ce pattern revient constamment dans les entretiens data analyst.
-- Employés sans département valide
SELECT e.nom
FROM employes e
LEFT JOIN departements d ON e.departement_id = d.id
WHERE d.id IS NULL;
| nom |
|---|
| Charlie |
| Diana |
Ce pattern « LEFT JOIN + WHERE IS NULL » est appelé anti-join et revient très souvent en entretien.
Exemple concret : audit de données clients
-- Clients sans commande depuis 6 mois (potentiel churn)
SELECT
c.nom_client,
c.email,
c.date_inscription,
MAX(co.date_commande) as derniere_commande
FROM clients c
LEFT JOIN commandes co ON c.id = co.client_id
AND co.date_commande >= DATE_SUB(NOW(), INTERVAL 6 MONTH)
WHERE co.id IS NULL
AND c.date_inscription < DATE_SUB(NOW(), INTERVAL 6 MONTH);
RIGHT JOIN : toutes les lignes de droite
RIGHT JOIN est le miroir du LEFT JOIN : il conserve toutes les lignes de la table de droite.
SELECT
e.nom,
d.nom_departement
FROM employes e
RIGHT JOIN departements d ON e.departement_id = d.id;
| nom | nom_departement |
|---|---|
| Alice | Marketing |
| Bob | Tech |
| NULL | Finance |
Le département Finance apparaît maintenant, même sans employé. En revanche, Charlie et Diana disparaissent car ils n'ont pas de correspondance dans la table de droite.
En pratique, le RIGHT JOIN est rarement utilisé. On préfère inverser les tables et utiliser un LEFT JOIN pour une meilleure lisibilité.
FULL OUTER JOIN : l'union complète
FULL OUTER JOIN renvoie toutes les lignes des deux tables, avec NULL là où il n'y a pas de correspondance.
SELECT
e.nom,
d.nom_departement
FROM employes e
FULL OUTER JOIN departements d ON e.departement_id = d.id;
| nom | nom_departement |
|---|---|
| Alice | Marketing |
| Bob | Tech |
| Charlie | NULL |
| Diana | NULL |
| NULL | Finance |
Toutes les lignes sont présentes. C'est le JOIN le plus inclusif.
💡 Bon à savoir
MySQL ne supporte pas FULL OUTER JOIN nativement. Il faut simuler avec un UNION de LEFT et RIGHT JOIN. PostgreSQL et SQL Server supportent FULL OUTER JOIN directement.
-- Simulation FULL OUTER JOIN en MySQL
SELECT e.nom, d.nom_departement
FROM employes e
LEFT JOIN departements d ON e.departement_id = d.id
UNION
SELECT e.nom, d.nom_departement
FROM employes e
RIGHT JOIN departements d ON e.departement_id = d.id;
Exemple concret : réconciliation comptable
-- Comparer factures émises vs paiements reçus
SELECT
COALESCE(f.numero_facture, p.reference_facture) as facture,
f.montant_facture,
p.montant_paye,
CASE
WHEN f.numero_facture IS NULL THEN 'Paiement sans facture'
WHEN p.reference_facture IS NULL THEN 'Facture impayée'
WHEN f.montant_facture = p.montant_paye THEN 'OK'
ELSE 'Différence montant'
END as statut
FROM factures f
FULL OUTER JOIN paiements p ON f.numero_facture = p.reference_facture;
CROSS JOIN : le produit cartésien
CROSS JOIN produit le produit cartésien : chaque ligne de la table de gauche est combinée avec chaque ligne de la table de droite.
SELECT e.nom, d.nom_departement
FROM employes e
CROSS JOIN departements d;
Avec 4 employés et 3 départements, vous obtenez 12 lignes. Le CROSS JOIN est utile pour générer des combinaisons (par exemple : tous les mois × tous les produits pour un rapport complet).
Exemple concret : génération de planning
-- Générer toutes les créneaux possibles pour les entretiens
SELECT
c.nom_candidat,
h.creneau_heure,
s.nom_salle
FROM candidats c
CROSS JOIN horaires_dispo h
CROSS JOIN salles s
WHERE c.statut = 'CONVOQUE'
AND h.date_creneau = '2024-03-15'
AND s.capacite >= 2
ORDER BY c.nom_candidat, h.creneau_heure;
⚠️ Attention
Le CROSS JOIN peut générer énormément de lignes très rapidement. Avec deux tables de 1000 lignes chacune, vous obtenez 1 million de lignes. Utilisez toujours des filtres WHERE appropriés.
SELF JOIN : une table avec elle-même
Le SELF JOIN consiste à joindre une table avec elle-même. C'est indispensable pour les structures hiérarchiques.
-- Table employes avec manager_id
SELECT
e.nom AS employe,
m.nom AS manager
FROM employes e
LEFT JOIN employes m ON e.manager_id = m.id;
Exemple concret : hiérarchie managériale
-- Afficher tous les employés avec leur chaîne hiérarchique
WITH RECURSIVE hierarchy AS (
-- Niveau 0 : PDG (pas de manager)
SELECT id, nom, manager_id, nom as chemin, 0 as niveau
FROM employes
WHERE manager_id IS NULL
UNION ALL
-- Niveaux suivants
SELECT e.id, e.nom, e.manager_id,
CONCAT(h.chemin, ' > ', e.nom) as chemin,
h.niveau + 1
FROM employes e
INNER JOIN hierarchy h ON e.manager_id = h.id
)
SELECT nom, chemin, niveau
FROM hierarchy
ORDER BY niveau, nom;
Autre cas classique en entretien : trouver les paires de clients dans la même ville.
SELECT
c1.nom AS client_1,
c2.nom AS client_2,
c1.ville
FROM clients c1
INNER JOIN clients c2
ON c1.ville = c2.ville
AND c1.id < c2.id; -- évite les doublons et auto-jointures
Joindre plus de deux tables
En entretien, les requêtes impliquent souvent 3 ou 4 tables. La logique est la même — on enchaîne les JOINs :
SELECT
e.nom AS employe,
d.nom_departement,
p.titre AS projet
FROM employes e
INNER JOIN departements d ON e.departement_id = d.id
INNER JOIN projets p ON e.id = p.employe_id;
Règle : chaque JOIN se fait entre une table déjà dans la requête et une nouvelle table. L'ordre des JOINs peut affecter les performances, mais pas le résultat (l'optimiseur SQL se charge généralement de réorganiser).
Exemple complexe : rapport de performance commercial
SELECT
v.nom_vendeur,
e.nom_equipe,
r.nom_region,
p.nom_produit,
SUM(c.montant) as ca_total,
COUNT(c.id) as nb_commandes,
AVG(c.montant) as panier_moyen
FROM vendeurs v
INNER JOIN equipes e ON v.equipe_id = e.id
INNER JOIN regions r ON v.region_id = r.id
LEFT JOIN commandes c ON v.id = c.vendeur_id
AND c.date_commande >= '2024-01-01'
LEFT JOIN produits p ON c.produit_id = p.id
WHERE v.statut = 'ACTIF'
GROUP BY v.id, v.nom_vendeur, e.nom_equipe, r.nom_region, p.nom_produit
HAVING ca_total > 10000 OR ca_total IS NULL
ORDER BY ca_total DESC NULLS LAST;
Les conditions de jointure composées
On peut joindre sur plusieurs colonnes :
SELECT *
FROM commandes c
INNER JOIN livraisons l
ON c.commande_id = l.commande_id
AND c.entrepot_id = l.entrepot_id;
On peut aussi utiliser des opérateurs de comparaison autres que = (non-equi join) :
-- Trouver les employés embauchés pendant une période promotionnelle
SELECT e.nom, p.nom_promo
FROM employes e
INNER JOIN promotions p
ON e.date_embauche BETWEEN p.date_debut AND p.date_fin;
Exemple avancé : jointure temporelle
-- Associer chaque vente au taux de change valide à cette date
SELECT
v.date_vente,
v.montant_local,
v.devise,
tc.taux_change,
v.montant_local * tc.taux_change as montant_eur
FROM ventes v
INNER JOIN taux_change tc ON v.devise = tc.devise
AND v.date_vente >= tc.date_debut
AND v.date_vente < COALESCE(tc.date_fin, '9999-12-31');
Erreurs fréquentes à éviter
1. Oublier les cas NULL
-- ERREUR : ne trouve pas les employés avec departement_id NULL
SELECT COUNT(*) FROM employes WHERE departement_id <> 999;
-- CORRECT : inclure explicitement les NULL
SELECT COUNT(*) FROM employes
WHERE departement_id <> 999 OR departement_id IS NULL;
2. Confondre WHERE et HAVING avec les JOINs
-- ERREUR : HAVING sans GROUP BY avec un JOIN
SELECT e.nom, d.nom_departement
FROM employes e
LEFT JOIN departements d ON e.departement_id = d.id
HAVING d.nom_departement IS NOT NULL;
-- CORRECT : utiliser WHERE
SELECT e.nom, d.nom_departement
FROM employes e
LEFT JOIN departements d ON e.departement_id = d.id
WHERE d.nom_departement IS NOT NULL;
3. Multiplications de lignes non intentionnelles
-- PROBLÈME : si un employé a plusieurs projets ET plusieurs formations,
-- on obtient le produit cartésien (3 projets × 2 formations = 6 lignes)
SELECT e.nom, p.nom_projet, f.nom_formation
FROM employes e
LEFT JOIN projets p ON e.id = p.employe_id
LEFT JOIN formations f ON e.id = f.employe_id;
-- SOLUTION : séparer en sous-requêtes ou utiliser des agrégations
SELECT
e.nom,
STRING_AGG(DISTINCT p.nom_projet, ', ') as projets,
STRING_AGG(DISTINCT f.nom_formation, ', ') as formations
FROM employes e
LEFT JOIN projets p ON e.id = p.employe_id
LEFT JOIN formations f ON e.id = f.employe_id
GROUP BY e.id, e.nom;
4. Ignorer la casse dans les comparaisons texte
💡 Bon à savoir
Dans certains SGBD, les comparaisons de texte sont sensibles à la casse. Utilisez UPPER() ou LOWER() pour des jointures robustes sur des champs texte.
-- Jointure robuste sur des champs texte
SELECT e.nom, d.nom_departement
FROM employes e
INNER JOIN departements d ON UPPER(e.code_dept) = UPPER(d.code);
5. Jointure sur des types de données incompatibles
-- ERREUR : joindre un VARCHAR avec un INT
SELECT * FROM produits p
INNER JOIN categories c ON p.categorie_nom = c.id;
-- CORRECT : convertir ou utiliser la bonne colonne
SELECT * FROM produits p
INNER JOIN categories c ON p.categorie_id = c.id;
Spécificités par SGBD
MySQL
- Pas de FULL OUTER JOIN natif → utiliser UNION
- Support des jointures STRAIGHT_JOIN pour forcer l'ordre
- Optimiseur moins sophistiqué que PostgreSQL/SQL Server
- Comparaisons de texte insensibles à la casse par défaut (dépend du collation)
PostgreSQL
- Excellent support de tous les types de JOINs
- LATERAL JOINs pour des sous-requêtes corrélées complexes
- Optimiseur très performant pour les requêtes multi-tables
- Extensions PostGIS pour des jointures spatiales avancées
SQL Server
- Support complet des JOINs standards
- CROSS APPLY / OUTER APPLY comme alternative aux LATERAL JOINs
- Plans d'exécution très détaillés pour optimiser les performances
- Index hints disponibles pour contrôler les stratégies de jointure
Oracle
- Support complet + syntaxe propriétaire avec (+) pour les outer joins
- Hints de jointure (/*+ USE_NL /, /+ USE_HASH */) pour contrôler l'optimiseur
- Partitioned joins pour les très gros volumes
- Materialized views pour optimiser les jointures fréquentes
SQLite
- Support limité : pas de FULL OUTER JOIN ni de RIGHT JOIN
- Optimiseur simple mais efficace pour les petites bases
- Jointures performantes sur les index B-tree
Exemples nominatifs par scénario métier
Marco, développeur e-commerce chez Zalando
Marco doit analyser les abandons de panier. Il utilise un LEFT JOIN pour conserver tous les paniers, même ceux sans commande finalisée :
-- Analyse des abandons de panier
SELECT
p.user_id,
p.session_id,
COUNT(pi.product_id) as nb_produits_panier,
SUM(pi.price) as valeur_panier,
CASE
WHEN c.order_id IS NULL THEN 'ABANDON'
ELSE 'CONVERTI'
END as statut
FROM panier p
LEFT JOIN panier_items pi ON p.id = pi.panier_id
LEFT JOIN commandes c ON p.id = c.panier_id
WHERE p.created_at >= '2024-01-01'
GROUP BY p.id, p.user_id, p.session_id, c.order_id
HAVING nb_produits_panier > 0
ORDER BY valeur_panier DESC;
Sophie, data analyst dans une banque française
Sophie analyse les comptes clients avec leurs produits souscrits. Elle utilise un FULL OUTER JOIN pour réconcilier deux sources de données :
-- Réconciliation clients CRM vs système bancaire
SELECT
COALESCE(crm.numero_client, sb.numero_client) as client,
crm.nom_complet,
sb.solde_total,
CASE
WHEN crm.numero_client IS NULL THEN 'Client technique sans CRM'
WHEN sb.numero_client IS NULL THEN 'Client CRM sans compte'
ELSE 'Client complet'
END as statut_reconciliation
FROM crm_clients crm
FULL OUTER JOIN systeme_bancaire sb ON crm.numero_client = sb.numero_client
WHERE crm.date_creation >= '2023-01-01' OR sb.date_ouverture >= '2023-01-01';
Ahmed, analyste logistique chez Amazon
Ahmed optimise les livraisons en analysant les retards par transporteur et région :
-- Analyse des retards de livraison
SELECT
t.nom_transporteur,
z.nom_zone,
COUNT(l.id) as nb_livraisons,
AVG(DATEDIFF(l.date_livraison_reelle, l.date_livraison_prevue)) as retard_moyen_jours,
SUM(CASE WHEN l.date_livraison_reelle > l.date_livraison_prevue THEN 1 ELSE 0 END) as nb_livraisons_retard
FROM commandes c
INNER JOIN livraisons l ON c.id = l.commande_id
INNER JOIN transporteurs t ON l.transporteur_id = t.id
INNER JOIN zones_livraison z ON c.code_postal BETWEEN z.cp_min AND z.cp_max
WHERE l.date_livraison_reelle IS NOT NULL
AND c.date_commande >= DATE_SUB(NOW(), INTERVAL 3 MONTH)
GROUP BY t.id, t.nom_transporteur, z.id, z.nom_zone
HAVING nb_livraisons >= 10
ORDER BY retard_moyen_jours DESC;
Validation et bonnes pratiques
1. Toujours vérifier les cardinalités
Avant d'écrire un JOIN, posez-vous ces questions :
- Relation 1:1, 1:N ou N:N ?
- Y a-t-il des valeurs NULL dans les clés de jointure ?
- Y a-t-il des valeurs orphelines ?
-- Vérification des cardinalités avant jointure
SELECT 'employes' as table_name, COUNT(*) as total, COUNT(DISTINCT departement_id) as nb_dept_uniques
FROM employes
UNION ALL
SELECT 'departements', COUNT(*), COUNT(DISTINCT id)
FROM departements;
2. Utiliser EXPLAIN pour optimiser
-- Analyser le plan d'exécution
EXPLAIN ANALYZE
SELECT e.nom, d.nom_departement
FROM employes e
INNER JOIN departements d ON e.departement_id = d.id
WHERE e.date_embauche > '2024-01-01';
3. Documenter les jointures complexes
-- Jointure métier : associer les ventes aux commissions par niveau hiérarchique
SELECT
v.nom_vendeur,
c.montant_commande,
-- Commission vendeur (taux 5%)
c.montant_commande * 0.05 as commission
FROM vendeurs v
INNER JOIN commandes c ON v.id = c.vendeur_id;
Tableau récapitulatif des JOINs
| Type de JOIN | Lignes retournées | Cas d'usage | Fréquence d'utilisation |
|---|---|---|---|
| INNER JOIN | Seulement les correspondances | Données complètes, aucune valeur manquante | ★★★★★ |
| LEFT JOIN | Toutes les lignes de gauche | Lister les données + valeurs orphelines | ★★★★★ |
| RIGHT JOIN | Toutes les lignes de droite | Cas rare (inverser les tables) | ★☆☆☆☆ |
| FULL OUTER JOIN | Toutes les lignes des deux tables | Réconciliation complète | ★★☆☆☆ |
| CROSS JOIN | Produit cartésien | Générer des combinaisons | ★★☆☆☆ |
| SELF JOIN | Dépend du type (INNER/LEFT) | Hiérarchies, paires de données | ★★★☆☆ |
Exercices corrigés
Exercice 1 : Anti-JOIN (LEFT JOIN + WHERE IS NULL)
Trouvez tous les produits qui n'ont jamais été commandés.
💡 Bon à savoir
Cet exercice teste votre compréhension du piège WHERE + LEFT JOIN. Il revient dans presque 80 % des entretiens selon nos données.
-- Solution
SELECT p.id, p.nom_produit
FROM produits p
LEFT JOIN lignes_commande lc ON p.id = lc.produit_id
WHERE lc.id IS NULL;
Exercice 2 : JOINs multiples avec GROUP BY
Calculez le nombre de commandes et le montant total par client, même pour ceux sans commande.
-- Solution
SELECT
c.id,
c.nom_client,
COUNT(co.id) as nb_commandes,
COALESCE(SUM(co.montant), 0) as montant_total
FROM clients c
LEFT JOIN commandes co ON c.id = co.client_id
GROUP BY c.id, c.nom_client
ORDER BY montant_total DESC;
Exercice 3 : SELF JOIN
Trouvez tous les couples d'employés du même département.
-- Solution
SELECT
e1.nom AS employe_1,
e2.nom AS employe_2,
d.nom_departement
FROM employes e1
INNER JOIN employes e2 ON e1.departement_id = e2.departement_id
INNER JOIN departements d ON e1.departement_id = d.id
WHERE e1.id < e2.id -- éviter les doublons
ORDER BY d.nom_departement, employe_1;
Exercice 4 : FULL OUTER JOIN
Comparez les listes de clients CRM et du système de facturation.
-- Solution en PostgreSQL/SQL Server
SELECT
COALESCE(crm.id, facturation.id) as client_id,
crm.nom as nom_crm,
facturation.nom as nom_facturation,
CASE
WHEN crm.id IS NULL THEN 'Seulement en facturation'
WHEN facturation.id IS NULL THEN 'Seulement en CRM'
ELSE 'Présent dans les deux'
END as statut
FROM clients_crm crm
FULL OUTER JOIN clients_facturation facturation
ON crm.id = facturation.id
ORDER BY client_id;
En MySQL, utiliser UNION à la place.
Exercice 5 : Jointure avec condition composée
Trouvez les employés affectés à un projet pendant une période donnée.
-- Solution
SELECT
e.nom,
p.nom_projet,
af.date_debut,
af.date_fin
FROM employes e
INNER JOIN affectations af ON e.id = af.employe_id
INNER JOIN projets p ON af.projet_id = p.id
WHERE af.date_debut <= '2024-03-31'
AND COALESCE(af.date_fin, '9999-12-31') >= '2024-01-01'
ORDER BY e.nom, af.date_debut;
Lien avec les autres concepts SQL
Les JOINs s'articulent avec d'autres concepts fondamentaux :
- GROUP BY et HAVING : essentiels pour agréger les données après un JOIN
- Sous-requêtes corrélées : alternative aux JOINs dans certains cas
- CTE (Common Table Expressions) : pour rendre les JOINs complexes plus lisibles
- Fonctions fenêtre : pour du ranking et des numérotations sans JOIN
- CASE WHEN : pour créer des colonnes conditionnelles après JOIN
- UNION / INTERSECT / EXCEPT : pour combiner des résultats de plusieurs JOINs
Questions fréquentes
Quelle est la différence entre WHERE et ON dans un JOIN ?
ON s'exécute pendant la jointure et détermine quelles lignes sont associées. WHERE s'exécute après la jointure et filtre le résultat. Avec un LEFT JOIN, une condition en WHERE peut éliminer les lignes NULL (valeur ajoutées) et transformer silencieusement le LEFT JOIN en INNER JOIN.
Pourquoi ma LEFT JOIN retourne moins de lignes que la table de gauche ?
Probablement parce que vous avez une condition WHERE sur la table de droite. Déplacez cette condition dans le ON, ou vérifiez que vous ne joignez pas sur plusieurs tables (problème de cardinalité N:N non gérée).
Comment optimiser une requête avec 5 JOINs ?
Utilisez EXPLAIN/EXPLAIN ANALYZE pour voir le plan d'exécution. Vérifiez que les colonnes de jointure sont indexées. Reordonnez les JOINs pour filtrer au maximum dès les premières jointures. Envisagez une approche par CTE pour améliorer la lisibilité et la performance.
Peux-je joindre sur un calcul ? Ex: ON a.prix * 1.1 = b.prix
Oui, mais c'est très inefficace. L'optimiseur ne peut pas utiliser d'index sur un calcul. Privilégiez l'approche inverse : calculer dans SELECT et filtrer en WHERE.
Qu'est-ce qu'un LATERAL JOIN ?
(PostgreSQL) Un LATERAL JOIN permet à une sous-requête de droite d'accéder aux colonnes de gauche, comme une boucle. Utile pour des calculs ligne par ligne complexes. MySQL l'approche avec CROSS JOIN + subqueries.
Pourquoi dois-je utiliser COALESCE dans FULL OUTER JOIN ?
Parce que quand une ligne n'existe que d'un côté, ses colonnes de l'autre côté sont NULL. COALESCE(a.id, b.id) vous donne l'ID quelle que soit sa source.
Quel est le JOIN le plus performant ?
INNER JOIN est généralement le plus rapide (moins de lignes à traiter). LEFT JOIN peut être lent si la table de gauche est énorme. Toujours indexer les colonnes de jointure (surtout les clés étrangères).
