SQL INNER JOIN : relier deux tables
Dans une base de données bien conçue, les informations sont réparties dans plusieurs tables : les commandes d'un côté, les clients de l'autre. Pour afficher « la commande n°12 a été passée par Léa Martin », il faut relier ces tables. C'est ce que fait une jointure, et INNER JOIN (ou simplement JOIN) est la plus courante.
Syntaxe
SELECT a.colonne, b.colonne
FROM table_a a
JOIN table_b b ON b.cle = a.cle_etrangere;Comment ça marche
Clé primaire et clé étrangère
Chaque client a un identifiant unique, sa clé primaire : clients.id. Dans la table commandes, la colonne client_id contient l'identifiant du client qui a passé la commande : c'est une clé étrangère. La jointure associe les lignes où commandes.client_id = clients.id.
Comment fonctionne INNER JOIN
Pour chaque ligne de la première table, SQL cherche les lignes de la seconde qui respectent la condition ON, et construit une ligne de résultat pour chaque correspondance. Une ligne sans correspondance disparaît du résultat : c'est la différence avec LEFT JOIN.
Les alias de tables
FROM commandes o JOIN clients c donne les surnoms o et c aux tables. On écrit ensuite c.nom ou o.id : c'est plus court, et indispensable quand deux tables ont une colonne du même nom.
Enchaîner les jointures
On peut ajouter autant de JOIN que nécessaire. Par exemple : lignes de commande → produits → fournisseurs.
Schéma
| id | client_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 9 |
| id | prenom |
|---|---|
| 1 | Léa |
| 2 | Hugo |
| 3 | Inès |
| commande | prenom |
|---|---|
| 1 | Léa |
| 2 | Hugo |
Schéma simplifié pour illustrer le principe. Les exemples ci-dessous utilisent la vraie base d'exercices de SQL Quest (une boutique en ligne fictive : voir le schéma complet).
Exemples avec leurs résultats
1. Une jointure simple
Chaque commande avec le prénom et le nom du client.
SELECT o.id AS commande, o.date_commande, c.prenom, c.nom
FROM commandes o
JOIN clients c ON c.id = o.client_id
LIMIT 6; Essayer dans le bac à sable| commande | date_commande | prenom | nom |
|---|---|---|---|
| 1 | 2023-06-02 | Chloé | Mercier |
| 2 | 2025-10-17 | Omar | Lambert |
| 3 | 2024-05-10 | Sarah | Lambert |
| 4 | 2025-02-08 | Léa | Lefebvre |
| 5 | 2025-09-06 | Manon | Garcia |
| 6 | 2024-01-11 | Inès | Haddad |
6 lignes au total
2. Joindre pour filtrer
Les commandes annulées des clients de Lyon.
SELECT o.id, c.prenom, c.nom, o.date_commande
FROM commandes o
JOIN clients c ON c.id = o.client_id
WHERE o.statut = 'annulée' AND c.ville = 'Lyon'; Essayer dans le bac à sable| id | prenom | nom | date_commande |
|---|---|---|---|
| 13 | Inès | Garcia | 2024-06-02 |
| 138 | Théo | Martin | 2023-12-01 |
| 153 | Théo | Dubois | 2023-06-01 |
| 174 | Théo | Michel | 2024-03-10 |
| 210 | Théo | Martin | 2023-10-20 |
| 348 | Chloé | Fontaine | 2025-04-15 |
| 358 | Sarah | Michel | 2025-08-15 |
| 368 | Théo | Michel | 2024-05-13 |
| 430 | Samir | Faure | 2023-07-22 |
| 500 | Théo | Michel | 2023-07-03 |
14 lignes au total · 10 affichées
3. Trois tables et un GROUP BY
Le chiffre d'affaires par catégorie de produit.
SELECT p.categorie, ROUND(SUM(l.quantite * l.prix_unitaire), 2) AS ca
FROM lignes_commande l
JOIN produits p ON p.id = l.produit_id
GROUP BY p.categorie
ORDER BY ca DESC; Essayer dans le bac à sable| categorie | ca |
|---|---|
| Informatique | 151 080 |
| Maison | 31 502,56 |
| Sport | 26 397,45 |
| Alimentation | 20 224,32 |
| Livres | 12 755,42 |
5 lignes au total
4. Une chaîne de jointures
Les produits avec le pays de leur fournisseur.
SELECT p.nom AS produit, f.nom AS fournisseur, f.pays
FROM produits p
JOIN fournisseurs f ON f.id = p.fournisseur_id
ORDER BY f.pays
LIMIT 8; Essayer dans le bac à sable| produit | fournisseur | pays |
|---|---|---|
| Souris sans fil | ElectroPlus | Allemagne |
| SSD 1To | ElectroPlus | Allemagne |
| Roman policier | BookWorld | Belgique |
| Haltères 5kg | AsiaParts | Chine |
| Corde à sauter | AsiaParts | Chine |
| Manga tome 1 | AsiaParts | Chine |
| Écran 27" | SportZone | Espagne |
| Hub USB-C | SportZone | Espagne |
8 lignes au total
Les erreurs fréquentes
Oublier la condition ON
Sans ON, chaque commande est associée à chaque client (produit cartésien) : 600 × 120 = 72 000 lignes.
SELECT * FROM commandes o JOIN clients c;SELECT * FROM commandes o JOIN clients c ON c.id = o.client_id;Relier les mauvaises colonnes
c.id = o.id associe la commande n°5 au client n°5, sans aucun rapport. La requête marche mais le résultat est faux.
SELECT * FROM commandes o JOIN clients c ON c.id = o.id;SELECT * FROM commandes o JOIN clients c ON c.id = o.client_id;Colonne ambiguë
Les deux tables ont une colonne id : SQL ne sait pas laquelle afficher (ambiguous column name). Préfixez-la avec l'alias.
SELECT id, nom FROM commandes o JOIN clients c ON c.id = o.client_id;SELECT o.id, c.nom FROM commandes o JOIN clients c ON c.id = o.client_id;Exercices
Essayez d'écrire la requête vous-même avant d'ouvrir la solution.
Défi 1. Affichez chaque ligne de commande avec le nom du produit et la quantité.
Voir une solution
SELECT l.commande_id, p.nom, l.quantite FROM lignes_commande l JOIN produits p ON p.id = l.produit_id LIMIT 10;| commande_id | nom | quantite |
|---|---|---|
| 1 | BD aventure | 5 |
| 1 | Gourde inox | 5 |
| 2 | Vase céramique | 2 |
| 3 | Haltères 5kg | 2 |
| 4 | Écran 27" | 4 |
| 4 | Thé vert | 4 |
| 4 | Corde à sauter | 4 |
| 4 | Cafetière | 4 |
10 lignes au total · 8 affichées
Essayer dans le bac à sableDéfi 2. Affichez le nombre de commandes gérées par chaque employé (prénom, nom, nombre).
Voir une solution
SELECT e.prenom, e.nom, COUNT(*) AS nb FROM commandes o JOIN employes e ON e.id = o.employe_id GROUP BY e.id ORDER BY nb DESC;| prenom | nom | nb |
|---|---|---|
| Samir | Roux | 49 |
| Inès | Mercier | 37 |
| Karim | Benali | 35 |
| Léa | Garcia | 33 |
| Louis | Chevalier | 33 |
| Lucas | Moreau | 32 |
| Chloé | Roux | 31 |
| Emma | Lambert | 31 |
21 lignes au total · 8 affichées
Essayer dans le bac à sableLes exercices interactifs sont corrigés automatiquement, avec un prof qui explique vos erreurs. Une leçon résumée reste affichée à côté de l'éditeur.
Questions fréquentes
JOIN et INNER JOIN, c'est pareil ?
Oui. INNER est implicite : JOIN tout court est une jointure interne.
Peut-on mettre la condition dans le WHERE plutôt que dans ON ?
Pour un INNER JOIN, le résultat est le même (c'est l'ancienne syntaxe FROM a, b WHERE a.x = b.y). Avec ON, la requête est plus lisible, et c'est indispensable pour les LEFT JOIN.
Pourquoi ma jointure donne-t-elle plus de lignes que prévu ?
Parce qu'une ligne peut avoir plusieurs correspondances : une commande contient plusieurs lignes de commande, donc elle apparaît plusieurs fois. C'est normal pour une relation « un à plusieurs ».
Combien de tables peut-on joindre ?
Autant que nécessaire. Chaque JOIN ajoute une table avec sa propre condition ON.