DataManip Insights ÉPISODE 1SQL & DonnéesGratuit
🗄️ Épisode 1 · Jointures

« J'ai fait un LEFT JOIN, donc j'ai tous mes clients. »
Vraiment ?

Un LEFT JOIN garde tous les clients de la table de gauche, sauf si un filtre placé au mauvais endroit les retire en silence. La requête tourne sans erreur, les chiffres ont l'air plausibles, et un tiers des clients a disparu.

🎯 L'analogie clé

Vous invitez tous vos clients à un événement et vous notez ce qu'ils commandent au buffet. Si vous ne gardez sur la liste que ceux qui ont commandé quelque chose, vous n'avez plus la liste des invités : vous avez la liste des consommateurs. Le piège est exactement celui-là.

Le principe

Trois jointures,
trois questions différentes.

Une jointure associe les lignes de deux tables sur une clé commune. Le type de jointure décide de ce qu'il advient des lignes sans correspondance.

🤝
INNER JOIN
Ne garde que les clients qui ont au moins une commande. Les autres disparaissent. C'est voulu quand on ne s'intéresse qu'aux acheteurs.
📋
LEFT JOIN
Garde tous les clients. Quand il n'y a pas de commande, les colonnes de la commande valent NULL. C'est la jointure de la liste complète.
🔍
Anti-jointure
LEFT JOIN puis WHERE commande.id IS NULL : les clients qui n'ont jamais commandé. Ici, le WHERE est le but.
Le point clé : un LEFT JOIN suivi d'un WHERE qui filtre une colonne de la table de droite se comporte comme un INNER JOIN. Les lignes NULL ne passent jamais le test.
À vous de manipuler

Déplacez le filtre,
comptez les clients.

Base jouet : des clients et leurs commandes sur l'année. La question : « tous les clients, avec leurs commandes depuis juillet ». Choisissez la requête et regardez ce qui revient. Les NULL apparaissent en gris.

Même question, trois requêtes
clientcommandemoismontant
Clients présents dans le résultat de chacune des trois requêtes

Les commandes sont tirées au hasard (1 à 3 par client ayant commandé). Le tableau montre les 7 premières lignes du résultat. La requête LEFT + WHERE donne exactement le même résultat que INNER + WHERE.

Clients dans le résultat
-
Clients perdus
-
par rapport à la liste complète
Lignes renvoyées
-
une ligne par commande
Lignes avec NULL
-
clients sans commande récente
À retenir : avec 600 clients dont 30 % ont commandé depuis juillet, LEFT + WHERE renvoie 190 clients, exactement comme INNER + WHERE. Le filtre dans ON en renvoie 600. Les deux premières requêtes perdent 410 clients sans le dire.
Ampleur de la perte

Plus le filtre est sélectif,
plus on perd.

Le nombre de clients renvoyés par LEFT + WHERE est le nombre de clients qui ont une commande dans la période : rien de plus. Si la période est courte, la liste fond. La courbe suit la part de clients ayant commandé récemment, avec les réglages précédents pour le reste.

Clients renvoyés selon la part de clients actifs
Nombre de clients dans le résultat (le point est le réglage actuel)

Les valeurs sont calculées en rejouant la requête sur une base de même taille, de 0 % à 85 % de clients actifs, avec la même part de clients sans aucune commande.

Si 10 % ont commandé
-
clients renvoyés par LEFT + WHERE
Si 50 % ont commandé
-
clients renvoyés par LEFT + WHERE
Avec le filtre dans ON
-
toujours la liste complète
Part de la liste perdue
-
au réglage actuel
Le point clé : le piège ne se voit pas dans les chiffres, parce qu'un résultat à 190 lignes est aussi plausible qu'un résultat à 600. Il se voit en comparant le nombre de clients avant et après la jointure.
En pratique

Quatre réflexes
avant de livrer une requête.

Voici comment écrire une jointure sans perdre de lignes en route, puis le code de la version correcte et de l'anti-jointure.

🔢
Comptez avant, comptez après
Comparez le nombre de clients de la table de gauche et celui du résultat. Un écart non voulu signale un filtre qui mord.
📍
Mettez le filtre dans ON
Une condition sur la table de droite, quand on veut garder toute la gauche, va dans la clause ON. Le WHERE reste pour la table de gauche.
🕳️
Cherchez les absents avec IS NULL
Pour trouver les clients sans commande, faites un LEFT JOIN puis filtrez sur la clé de droite IS NULL.
✍️
Écrivez le type de jointure
JOIN seul vaut INNER JOIN. Écrire le type rend l'intention lisible et la relecture possible.
Python · jointures.sql
-- Tous les clients, avec leurs commandes depuis juillet (version correcte)
SELECT c.id, c.nom, o.id AS commande_id, o.montant
FROM clients c
LEFT JOIN commandes o
       ON o.client_id = c.id
      AND o.mois >= 7;          -- le filtre est dans ON

-- Clients qui n'ont jamais commandé : anti-jointure
SELECT c.id, c.nom
FROM clients c
LEFT JOIN commandes o ON o.client_id = c.id
WHERE o.id IS NULL;

-- Contrôle : combien de clients avant, combien après ?
SELECT COUNT(*) FROM clients;
SELECT COUNT(DISTINCT c.id) FROM clients c LEFT JOIN commandes o ON o.client_id = c.id AND o.mois >= 7;
Test rapide

Vérifiez votre compréhension.

On veut tous les clients avec le total de leurs commandes de 2026, y compris ceux sans commande. Le résultat contient trop peu de clients. Quelle est la cause la plus probable ?

Ce qu'il faut retenir

Épisode suivant

L'ordre d'exécution d'une requête

On écrit SELECT en premier, la base le lit en dernier : pourquoi un alias n'existe pas dans le WHERE, et ce que cela change.

ACCÈS COMPLET

Cet épisode est gratuit. Les 282 autres t'attendent.

Statistiques, finance quantitative, machine learning, deep learning, LLM, agents IA, MLOps : 321 épisodes avec des simulateurs à manipuler, pas des vidéos.

99 € (+ TVA selon le pays) · accès à vie · remboursement possible sous 7 jours
Débloquer les 282 épisodes · 99 € J'ai déjà une clé de licence
FULL ACCESS

This episode is free. The other 282 are waiting for you.

Statistics, quantitative finance, machine learning, deep learning, LLMs, AI agents, MLOps: 321 interactive episodes with simulators to play with, not videos.

€99 (+ VAT depending on country) · lifetime access · 7-day refund
Unlock the 282 episodes · €99 I already have a license key