Qu’est-ce qu’une jointure SQL (JOIN) ?
Une jointure est une opération qui combine les lignes de deux ou plusieurs tables basée sur une relation logique entre elles.
Analogie : C’est comme assembler deux listes en les reliant par un point commun (ID département, email, etc.).
Exemple basique :
SELECT e.nom, d.nom_dept FROM employes e INNER JOIN departements d ON e.dept_id = d.id;
Pourquoi : Les données relationnelles sont réparties sur plusieurs tables. Les jointures permettent de reconstituer l’information complète.
Quel est le mnémonique pour retenir les types de JOIN ?
Mnémonique : ILRFC (I Love Really Fast Cars)
Bonus mnémonique pour LEFT JOIN :
“LEFT = Liste Exhaustive Fait Tout” → Utilisez LEFT quand vous voulez TOUT d’un côté
Quelle est la différence entre INNER JOIN et LEFT JOIN ?
Tableau comparatif :
Exemple visuel :
```
– 4 employés (dont David sans dept_id)
– 3 départements (dont Marketing sans employés)
INNER JOIN → 3 résultats (Alice, Bob, Claire)
LEFT JOIN → 4 résultats (Alice, Bob, Claire, David avec NULL)
~~~
Aspect | INNER JOIN | LEFT JOIN |
|——–|————|———–|
| Résultat | Intersection | Tout à gauche + correspondances |
| Lignes sans correspondance | ❌ Éliminées | ✅ Gardées (NULL à droite) |
| Usage | 80% des cas | 18% des cas |
| Performance | ⚡ Plus rapide | 🐌 Plus lent |
| Diagramme | ◐◑ (milieu) | ◐● (gauche pleine) |
Comment trouver les employés SANS département assigné ?
Technique classique : LEFT JOIN + WHERE NULL
SELECT e.nom FROM employes e LEFT JOIN departements d ON e.dept_id = d.id WHERE d.id IS NULL;
Explication étape par étape :
1. LEFT JOIN garde TOUS les employés
2. Pour les employés sans département, d.id sera NULL
3. WHERE d.id IS NULL filtre seulement ces cas
Erreur courante : Ne pas utiliser INNER JOIN pour cela !
-- ❌ Mauvais : retourne 0 résultats SELECT e.nom FROM employes e INNER JOIN departements d ON e.dept_id = d.id WHERE d.id IS NULL;
Pourquoi : INNER JOIN élimine déjà les lignes sans correspondance AVANT le WHERE.
Qu’est-ce qu’un CROSS JOIN et quand l’utiliser ?
CROSS JOIN = produit cartésien, chaque ligne de A avec chaque ligne de B.
Formule : Si A a 3 lignes et B a 4 lignes → 3 × 4 = 12 résultats
Exemple concret :
```
– Générer toutes les combinaisons taille × couleur
SELECT t.taille, c.couleur
FROM tailles t
CROSS JOIN couleurs c;
– 3 tailles (S, M, L) × 2 couleurs (Rouge, Bleu) = 6 combinaisons
– S | Rouge
– S | Bleu
– M | Rouge
– M | Bleu
– L | Rouge
– L | Bleu
```
Cas d’usage :
- Générer des variantes de produits
- Créer des matrices (employés × périodes)
- Données de référence combinatoires
DANGER : Peut générer énormément de lignes (1000 × 1000 = 1,000,000) !
Qu’est-ce qu’un SELF JOIN et comment l’utiliser ?
SELF JOIN = joindre une table avec elle-même, essentiel pour les hiérarchies.
Cas d’usage typique : organigramme (employé → manager)
```
CREATE TABLE employes (
id INT,
nom VARCHAR(100),
manager_id INT – Référence vers un autre employé
);
– SELF JOIN : afficher employé et son manager
SELECT
e.nom as employe,
m.nom as manager
FROM employes e
LEFT JOIN employes m ON e.manager_id = m.id;
– Résultat :
– Alice | NULL (PDG)
– Bob | Alice (manager de Bob)
– Claire | Alice (manager de Claire)
```
Astuce clé : Utilisez des alias différents (e et m) pour distinguer les deux “versions” de la table.
Autres cas d’usage :
- Trouver des collègues (même département)
- Comparer des lignes entre elles
- Relations symétriques (amitiés)
Quelle est la différence entre FULL OUTER JOIN et UNION de LEFT/RIGHT ?
FULL OUTER JOIN retourne toutes les lignes des deux tables avec correspondances alignées.
Équivalence avec UNION :
```
– FULL OUTER JOIN (natif si supporté)
SELECT e.nom, d.nom_dept
FROM employes e
FULL OUTER JOIN departements d ON e.dept_id = d.id;
– Équivalent avec UNION (MySQL)
SELECT e.nom, d.nom_dept FROM employes e LEFT JOIN departements d ON e.dept_id = d.id
UNION
SELECT e.nom, d.nom_dept FROM employes e RIGHT JOIN departements d ON e.dept_id = d.id;
```
Résultat :
- Alice | IT
- Bob | RH
- David | NULL (employé sans département)
- NULL | Marketing (département sans employés)
Cas d’usage : Audits, réconciliation de données, trouver les incohérences des DEUX côtés.
Quel est le piège avec WHERE vs ON dans un LEFT JOIN ?
Problème : WHERE filtre APRÈS la jointure, ON filtre PENDANT.
❌ Mauvais (WHERE annule le LEFT JOIN) :
SELECT e.nom, d.nom_dept FROM employes e LEFT JOIN departements d ON e.dept_id = d.id WHERE d.ville = 'Paris'; -- Résultat : seulement employés de depts à Paris -- (Équivalent à INNER JOIN !)
✅ Correct (ON préserve le LEFT JOIN) :
SELECT e.nom, d.nom_dept FROM employes e LEFT JOIN departements d ON e.dept_id = d.id AND d.ville = 'Paris'; -- Résultat : TOUS les employés, nom_dept NULL si pas Paris
Règle d’or : Avec LEFT JOIN, mettez les conditions sur la table de DROITE dans ON, pas WHERE.
Que va afficher ce code avec jointures multiples ?
Code :
SELECT e.nom, d.nom_dept, v.nom_ville FROM employes e INNER JOIN departements d ON e.dept_id = d.id INNER JOIN villes v ON d.ville_id = v.id;
Résultat : Employés avec leur département ET la ville du département.
Ordre d’exécution :
1. employes INNER JOIN departements → Résultat temporaire T1 (employés avec dept)
2. T1 INNER JOIN villes → Résultat final (employés avec dept et ville)
Exemple de résultat :
Alice | IT | Paris Bob | RH | Lyon Claire | IT | Paris
Important : Les jointures s’enchaînent de gauche à droite. Chaque jointure travaille sur le résultat de la précédente.
Comment éviter les doublons dans une jointure 1-à-plusieurs ?
Problème : Jointures 1-à-plusieurs créent des duplications.
❌ Exemple du problème :
-- Département IT : 2 employés et 3 projets -- Résultat : 2 × 3 = 6 lignes pour IT ! SELECT d.nom_dept, e.nom, p.nom_projet FROM departements d LEFT JOIN employes e ON d.id = e.dept_id LEFT JOIN projets p ON d.id = p.dept_id;
✅ Solutions :
Option 1 : Agrégation
SELECT d.nom_dept,
COUNT(DISTINCT e.id) as nb_employes,
COUNT(DISTINCT p.id) as nb_projets
FROM departements d
LEFT JOIN employes e ON d.id = e.dept_id
LEFT JOIN projets p ON d.id = p.dept_id
GROUP BY d.id, d.nom_dept;Option 2 : Sous-requêtes
```
SELECT d.nom_dept,
(SELECT COUNT() FROM employes WHERE dept_id = d.id) as nb_employes,
(SELECT COUNT() FROM projets WHERE dept_id = d.id) as nb_projets
FROM departements d;
~~~
Comment optimiser les performances d’une jointure ?
Techniques d’optimisation essentielles :
1. Index sur les colonnes de jointure
-- ✅ Créer des index sur les clés étrangères CREATE INDEX idx_dept ON employes(dept_id); CREATE INDEX idx_ville ON departements(ville_id);
2. SELECT seulement les colonnes nécessaires
```
– ❌ Lent
SELECT * FROM employes e JOIN departements d ON e.dept_id = d.id;
– ✅ Rapide
SELECT e.nom, d.nom_dept FROM employes e JOIN departements d ON e.dept_id = d.id;
```
3. Utiliser INNER JOIN plutôt que LEFT JOIN si possible
-- INNER JOIN est plus rapide (moins de lignes)
4. Mettre les conditions dans ON, pas après WHERE
-- ✅ Bon FROM employes e INNER JOIN departements d ON e.dept_id = d.id
5. Analyser avec EXPLAIN
```
EXPLAIN SELECT … FROM … JOIN …;
~~~
Quelle est la différence entre une jointure implicite et explicite ?
Tableau comparatif :
Jointure implicite (à éviter) :
-- Ancienne syntaxe, difficile à lire SELECT e.nom, d.nom_dept FROM employes e, departements d WHERE e.dept_id = d.id;
Jointure explicite (recommandée) :
-- Syntaxe moderne, claire et lisible SELECT e.nom, d.nom_dept FROM employes e INNER JOIN departements d ON e.dept_id = d.id;
Pourquoi éviter l’implicite :
- Mélange les conditions de jointure et de filtrage
- Risque de produit cartésien si on oublie WHERE
- Moins lisible pour les équipes
Aspect | Implicite (ancienne) | Explicite (moderne) |
|——–|———————|——————-|
| Syntaxe | FROM t1, t2 WHERE | FROM t1 JOIN t2 ON |
| Lisibilité | ❌ Difficile | ✅ Claire |
| Recommandation | ⛔ Éviter | ✅ Utiliser |
Comment faire une jointure sur une plage de dates ?
Technique : jointure avec BETWEEN
Exemple : ventes par période d’évaluation
```
CREATE TABLE ventes (
id INT,
employe_id INT,
date_vente DATE,
montant DECIMAL(10,2)
);
CREATE TABLE periodes (
nom_periode VARCHAR(50),
date_debut DATE,
date_fin DATE
);
– Jointure sur plage de dates
SELECT
e.nom,
pe.nom_periode,
SUM(v.montant) as total
FROM employes e
CROSS JOIN periodes pe – Toutes les combinaisons employé-période
LEFT JOIN ventes v
ON v.employe_id = e.id
AND v.date_vente BETWEEN pe.date_debut AND pe.date_fin
GROUP BY e.id, e.nom, pe.nom_periode;
```
Résultat : Matrice employés × périodes avec totaux de ventes.
Astuce : CROSS JOIN crée la grille, LEFT JOIN remplit les données.
Quel est le piège du NATURAL JOIN et pourquoi l’éviter ?
NATURAL JOIN joint automatiquement sur TOUTES les colonnes de même nom.
Exemple :
```
– Joint automatiquement sur dept_id
SELECT nom, nom_dept
FROM employes
NATURAL JOIN departements;
– Équivalent à :
SELECT e.nom, d.nom_dept
FROM employes e
INNER JOIN departements d ON e.dept_id = d.dept_id;
```
DANGERS :
❌ Si une nouvelle colonne de même nom est ajoutée, la jointure change silencieusement
-- Si on ajoute 'ville' aux deux tables, NATURAL JOIN jointra aussi sur ville ! -- Résultat : données incorrectes sans erreur
❌ Difficile à maintenir et déboguer
❌ Peu utilisé en production (< 1%)
✅ Recommandation : Toujours utiliser des jointures explicites avec ON.
Comment utiliser EXISTS avec une jointure ?
EXISTS teste l’existence d’au moins une ligne, souvent plus performant que JOIN pour certains cas.
Cas d’usage : Départements ayant des employés
Option 1 : INNER JOIN + DISTINCT
SELECT DISTINCT d.nom_dept FROM departements d INNER JOIN employes e ON d.id = e.dept_id;
Option 2 : EXISTS (souvent plus rapide)
SELECT d.nom_dept
FROM departements d
WHERE EXISTS (
SELECT 1 FROM employes e WHERE e.dept_id = d.id
);Avantages de EXISTS :
- ⚡ S’arrête dès la première correspondance trouvée
- ✅ Pas besoin de DISTINCT (pas de doublons)
- ✅ Plus clair pour “au moins un”
Quand utiliser :
- Test d’existence (“départements qui ont des employés”)
- Filtrage (“clients qui ont commandé”)
- Meilleure performance pour grandes tables