Leçon 5.8 · Temps de lecture : ~8 min
Cette leçon rassemble des scénarios pratiques de SQL JOIN : joindre plusieurs tables en suivant les relations du schéma, agréger des données jointes, trouver des données manquantes, et éviter les deux erreurs de jointure les plus courantes : les totaux gonflés et les jointures externes qui perdent leurs lignes.
Scénarios pratiques de SQL JOIN
Vous connaissez les types de jointures ; il s'agit maintenant de les appliquer à de vraies questions. La plupart des requêtes de rapport suivent les mêmes étapes : trouver les tables qui contiennent les données, les relier par leurs clés, filtrer et agréger.
Planifier le chemin de jointure
Partez de la question et trouvez un chemin entre les tables dans le diagramme de la base. « Le chiffre d'affaires par catégorie de film » nécessite category, puis film_category, inventory, rental et enfin payment :
SELECT c.name AS category,
COUNT(p.payment_id) AS payments,
SUM(p.amount) AS revenue
FROM category AS c
INNER JOIN film_category AS fc ON fc.category_id = c.category_id
INNER JOIN inventory AS i ON i.film_id = fc.film_id
INNER JOIN rental AS r ON r.inventory_id = i.inventory_id
INNER JOIN payment AS p ON p.rental_id = r.rental_id
GROUP BY c.name
ORDER BY revenue DESC;
Résultat : 16 catégories triées par chiffre d'affaires ; Sports et Sci-Fi arrivent en tête.
La table film elle-même n'est pas nécessaire : film_category et inventory stockent toutes deux film_id, on peut donc les joindre directement. Ne joignez que les tables qui contiennent des données ou qui sont nécessaires pour les relier.
Agréger après une jointure
Le total des dépenses de chaque client :
SELECT c.customer_id,
c.first_name,
c.last_name,
COUNT(*) AS payments,
SUM(p.amount) AS total_spent
FROM customer AS c
INNER JOIN payment AS p ON p.customer_id = c.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name
ORDER BY total_spent DESC
LIMIT 10;
Résultat : les 10 clients qui ont le plus dépensé.
Groupez par la clé (customer_id), pas seulement par le nom : deux clients différents peuvent avoir le même nom.
Le piège de la multiplication des lignes : des totaux gonflés
Une jointure multiplie les lignes : un client avec 30 paiements et 30 locations devient 900 lignes si les deux tables sont jointes au client en même temps. Toute somme sur un tel résultat est fausse.
-- Faux : les paiements sont multipliés par les locations
SELECT c.customer_id,
SUM(p.amount) AS total_spent
FROM customer AS c
INNER JOIN payment AS p ON p.customer_id = c.customer_id
INNER JOIN rental AS r ON r.customer_id = c.customer_id
WHERE c.customer_id = 1
GROUP BY c.customer_id;
Résultat : 3 797,76 au lieu des 118,68 réels : chacun des 32 paiements du client a été compté une fois pour chacune de ses 32 locations.
La règle : ne joignez pas deux tables « plusieurs » indépendantes à la même ligne dans une seule requête. Ici, la table rental n'est pas du tout nécessaire. Quand vous avez besoin de totaux issus de deux tables de ce type, agrégez d'abord chacune séparément ; vous verrez comment avec les sous-requêtes et les CTE dans le module 6.
Trouver des données manquantes
LEFT JOIN ... WHERE right_key IS NULL trouve les lignes sans correspondance. Les clients qui n'ont rien loué depuis le 23 août 2005 :
SELECT c.customer_id, c.first_name, c.last_name
FROM customer AS c
LEFT JOIN rental AS r ON r.customer_id = c.customer_id
AND r.rental_date >= '2005-08-23'
WHERE r.rental_id IS NULL
ORDER BY c.customer_id;
Résultat : les clients sans location dans les derniers jours des données.
Notez la condition de date : elle est dans ON, car elle limite les locations qui comptent comme correspondance. Dans WHERE, elle supprimerait tous les clients sans correspondance, et la requête ne renverrait rien.
WHERE ou ON dans les jointures externes
L'exemple précédent illustre la règle générale pour LEFT JOIN :
| Condition sur | À placer dans | Effet |
|---|---|---|
| la table de droite, pour limiter les correspondances | ON | Les lignes de gauche restent ; seules les correspondances sont filtrées |
| la table de gauche | WHERE | Les lignes de gauche sont retirées du résultat |
la clé de la table de droite IS NULL | WHERE | Ne garde que les lignes sans correspondance (anti-jointure) |
Pour INNER JOIN, ON et WHERE donnent le même résultat ; l'emplacement est donc une question de lisibilité.
Points clés :
- Planifiez le chemin de jointure dans le schéma et ne joignez que les tables nécessaires.
- Groupez par les clés, pas seulement par les noms.
- Joindre deux tables « plusieurs » à la même ligne multiplie les lignes et gonfle les sommes et les comptages.
LEFT JOIN ... WHERE key IS NULLtrouve les données manquantes.- Dans les jointures externes, les conditions sur la table de droite vont dans
ON.
Questions fréquentes
Comment joindre plus de deux tables en SQL ?
Ajoutez un JOIN ... ON ... par table, en suivant les clés étrangères du schéma. Chaque jointure relie une nouvelle table à celles déjà présentes dans la requête.
Pourquoi mon SUM renvoie-t-il un nombre beaucoup plus grand après un JOIN ?
Parce que la jointure a multiplié les lignes : chaque ligne d'une table a été répétée pour chaque ligne correspondante d'une autre. Supprimez les tables inutiles, ou agrégez chaque table séparément avant de joindre les résultats.
Un filtre doit-il aller dans ON ou dans WHERE ?
Pour INNER JOIN, cela ne change rien. Pour LEFT JOIN, une condition sur la table de droite va dans ON ; dans WHERE, elle supprime les lignes sans correspondance et transforme la jointure externe en jointure interne.