Développement Web

Jointures SQL : INNER, LEFT et auto-jointure pas à pas

12 دقائق للقراءة
📍 À lire d abord : Apprendre le SQL : le guide complet pour débuter. Ce tutoriel approfondit les jointures, deuxième brique du parcours.

Dans une base bien conçue, l information est répartie : le nom d un client vit dans la table clients, ses achats dans commandes, et le détail des articles dans lignes_commande. Pour répondre à une question aussi banale que « quels produits le client Awa a-t-elle commandés ? », il faut rassembler ces morceaux. C est exactement le rôle des jointures, l opération qui fait la véritable puissance du modèle relationnel.

Ce tutoriel vous apprend à relier des tables sans produire de doublons ni perdre de lignes. Nous distinguons la jointure interne, qui ne garde que les correspondances, la jointure externe gauche, qui conserve tout même sans correspondance, et l auto-jointure, où une table se relie à elle-même. Le fil conducteur reste la boutique TechStock.

🎯 Ce que vous allez apprendre

  • Comprendre ce qu est un produit cartésien et pourquoi il faut l éviter.
  • Écrire une INNER JOIN pour combiner deux tables sur leur clé commune.
  • Utiliser LEFT JOIN pour conserver les lignes sans correspondance.
  • Détecter les absences (clients sans commande, produits jamais vendus).
  • Enchaîner plusieurs jointures sur trois tables et plus.
  • Relier une table à elle-même avec une auto-jointure.

🛠️ Ce que vous allez construire

Vous allez produire les requêtes qui alimentent un écran de suivi des commandes : la liste des commandes avec le nom du client, le détail de chaque commande ligne par ligne, la liste des clients inactifs, et l organigramme de l équipe. Autant de vues indispensables au pilotage de la boutique.

Prérequis

  • Maîtriser SELECT, WHERE et ORDER BY (voir le tutoriel interroger une table).
  • La base TechStock créée, avec les produits déjà insérés.
  • ⏱️ Temps estimé : environ 50 minutes.

Ajoutons des clients, des commandes, leurs lignes, et une table d employés pour l auto-jointure :

INSERT INTO clients (nom, ville) VALUES
  ('Awa Ndiaye', 'Dakar'),
  ('Karim Touré', 'Abidjan'),
  ('Fatou Bâ', 'Dakar'),
  ('Moussa Diarra', 'Bamako');  -- ce client ne commandera pas

INSERT INTO commandes (client_id, statut) VALUES
  (1, 'validee'), (1, 'validee'), (2, 'validee'), (3, 'en_attente');

INSERT INTO lignes_commande (commande_id, produit_id, quantite, prix_unitaire) VALUES
  (1, 1, 1, 45.00), (1, 3, 2, 12.00),
  (2, 6, 1, 110.00),
  (3, 4, 3, 39.90),
  (4, 5, 5, 6.00);

CREATE TABLE employes (
  id         SERIAL PRIMARY KEY,
  nom        VARCHAR(80) NOT NULL,
  poste      VARCHAR(60),
  manager_id INTEGER REFERENCES employes(id)
);
INSERT INTO employes (nom, poste, manager_id) VALUES
  ('Sophie Mendy', 'Directrice', NULL),
  ('Ibrahima Sow', 'Responsable ventes', 1),
  ('Aminata Fall', 'Vendeuse', 2),
  ('Cheikh Diop', 'Vendeur', 2);

Étape 1 — Le danger du produit cartésien

Pour comprendre pourquoi la condition de jointure est vitale, voyons d abord ce qui se passe sans elle. Si l on liste deux tables sans préciser comment les relier, le moteur associe chaque ligne de la première à toutes les lignes de la seconde. C est le produit cartésien, ou CROSS JOIN.

-- À NE PAS faire en production : explosion du nombre de lignes
SELECT clients.nom, commandes.id
FROM clients, commandes;

Avec quatre clients et quatre commandes, on obtient seize lignes sans aucun sens : chaque client est faussement associé à chaque commande, y compris celles des autres. Sur de vraies tables, ce croisement produit des millions de lignes parasites. La leçon est simple : combiner deux tables exige toujours d indiquer la colonne qui les relie.

Point d étape — Lancez cette requête et comptez les lignes : seize, soit quatre fois quatre. Ce nombre vous rappellera toujours qu une jointure sans condition est une erreur.

Étape 2 — La jointure interne avec INNER JOIN

La jointure interne relie deux tables en ne conservant que les paires de lignes qui se correspondent selon une condition. On écrit le nom de la première table, puis JOIN, la seconde table, et enfin ON suivi de l égalité des clés. Pour associer chaque commande à son client, on relie commandes.client_id à clients.id.

SELECT clients.nom, commandes.id AS num_commande, commandes.statut
FROM commandes
JOIN clients ON clients.id = commandes.client_id;

Le résultat ne contient que des associations réelles : chaque commande apparaît à côté du bon client. Le mot INNER est facultatif, JOIN seul signifie déjà jointure interne. Notez que le client Moussa Diarra, qui n a passé aucune commande, n apparaît pas : la jointure interne écarte les lignes sans correspondance.

Point d étape — Vous devez voir quatre lignes, chacune portant le nom du bon client. Awa apparaît deux fois car elle a deux commandes.

Étape 3 — Les alias de tables

Répéter le nom complet des tables alourdit vite la requête. On leur donne des alias courts, déclarés avec AS (souvent omis), puis utilisés partout ailleurs. C est une convention universelle qui rend les jointures bien plus lisibles, surtout quand elles se multiplient.

SELECT c.nom, cmd.id AS num_commande, cmd.statut
FROM commandes AS cmd
JOIN clients AS c ON c.id = cmd.client_id;

Le résultat est identique, mais la requête se lit mieux. Quand deux tables possèdent une colonne de même nom, comme id, l alias devient même indispensable pour lever l ambiguïté : écrire seulement id provoquerait une erreur, car le moteur ne saurait pas de quelle table il s agit.

Étape 4 — La jointure externe gauche avec LEFT JOIN

La jointure interne a un angle mort : elle fait disparaître les lignes sans correspondance. Or on veut souvent les conserver. « Lister tous les clients avec leur nombre de commandes, y compris ceux qui n ont jamais commandé » exige de garder Moussa Diarra. C est le rôle de LEFT JOIN, qui conserve toutes les lignes de la table de gauche et complète avec des NULL là où la table de droite n a pas de correspondance.

SELECT c.nom, cmd.id AS num_commande
FROM clients AS c
LEFT JOIN commandes AS cmd ON cmd.client_id = c.id
ORDER BY c.nom;

Cette fois, Moussa Diarra figure bien dans le résultat, avec un num_commande à NULL : la preuve qu il existe comme client mais n a pas de commande associée. La table de gauche est celle écrite avant LEFT JOIN ; toutes ses lignes sont garanties dans le résultat.

Point d étape — Le résultat doit contenir au moins une ligne pour chaque client, dont Moussa avec une valeur NULL. Si Moussa manque, vous avez sans doute écrit JOIN au lieu de LEFT JOIN.

Étape 5 — Trouver les absences

Le LEFT JOIN ouvre une technique très utile : repérer ce qui n a pas de correspondance. Puisque les lignes sans correspondance portent un NULL côté table de droite, il suffit de filtrer sur ce NULL. Pour lister les clients sans aucune commande :

SELECT c.nom, c.ville
FROM clients AS c
LEFT JOIN commandes AS cmd ON cmd.client_id = c.id
WHERE cmd.id IS NULL;

Le résultat ne contient plus que Moussa Diarra. Le mécanisme mérite qu on s y arrête : la jointure ramène d abord tous les clients, puis le filtre WHERE ne garde que ceux dont la commande est absente. La même approche révèle les produits jamais vendus, en partant de produits et en joignant lignes_commande. C est l un des usages les plus précieux des jointures externes.

Point d étape — Une seule ligne attendue : Moussa Diarra de Bamako. Ce résultat confirme que vous savez transformer une jointure externe en détecteur d absence.

Étape 6 — Joindre trois tables

Les vraies questions traversent souvent plus de deux tables. « Quels produits le client Awa a-t-elle commandés ? » relie clients, commandes, lignes_commande et produits. On enchaîne simplement les JOIN, chacun avec sa condition ON. Le moteur les applique de proche en proche.

SELECT c.nom AS client, p.nom AS produit, lc.quantite
FROM clients AS c
JOIN commandes AS cmd       ON cmd.client_id = c.id
JOIN lignes_commande AS lc  ON lc.commande_id = cmd.id
JOIN produits AS p          ON p.id = lc.produit_id
WHERE c.nom = 'Awa Ndiaye'
ORDER BY p.nom;

Chaque jointure ajoute une table au chemin : du client vers ses commandes, des commandes vers leurs lignes, des lignes vers les produits. Le filtre final restreint à Awa. C est ainsi que SQL reconstitue une information dispersée en une réponse unique et lisible.

Point d étape — Vous devez obtenir la liste des articles achetés par Awa, à raison d une ligne par produit commandé. Si vous voyez des doublons inattendus, vérifiez chaque condition ON.

Étape 7 — L auto-jointure

Parfois la table à relier est la table elle-même. Dans employes, la colonne manager_id pointe vers l id d un autre employé : le supérieur hiérarchique. Pour afficher chaque employé à côté du nom de son manager, on joint la table à elle-même en lui donnant deux alias différents, comme s il s agissait de deux tables distinctes.

SELECT e.nom AS employe, m.nom AS manager
FROM employes AS e
LEFT JOIN employes AS m ON m.id = e.manager_id
ORDER BY e.nom;

L alias e représente l employé, l alias m son manager. On utilise LEFT JOIN pour que la directrice, qui n a pas de supérieur (manager_id NULL), apparaisse quand même, avec un manager à NULL. L auto-jointure est la clé des structures hiérarchiques : organigrammes, catégories imbriquées, fils de discussion.

Point d étape — Quatre employés affichés ; Sophie Mendy a un manager NULL, les deux vendeurs ont Ibrahima Sow comme manager. Cet organigramme valide votre auto-jointure.

Et les jointures à droite et complètes ?

Par symétrie, RIGHT JOIN conserve toutes les lignes de la table de droite, et FULL JOIN celles des deux côtés. En pratique, on s en sert peu : il suffit d inverser l ordre des tables pour transformer un RIGHT JOIN en LEFT JOIN, plus intuitif. SQLite, par exemple, n a longtemps pas supporté RIGHT et FULL. Retenez surtout LEFT JOIN, qui couvre la quasi-totalité des besoins de jointure externe.

🐞 Pièges fréquents

Symptôme Cause probable Correctif
Nombre de lignes qui explose Condition ON oubliée (produit cartésien) Toujours préciser ON
Erreur colonne ambiguë Même nom de colonne dans deux tables Préfixer par l alias de table
Lignes attendues manquantes INNER JOIN au lieu de LEFT JOIN Choisir le bon type de jointure
Doublons inattendus Relation un-à-plusieurs Agréger ou affiner les conditions
Filtre annule le LEFT JOIN Condition sur la table de droite dans WHERE Déplacer la condition dans le ON

🌍 Réalités du terrain

Une jointure mal maîtrisée est l une des premières causes de lenteur dans une application. Sur une connexion limitée et un serveur modeste, une requête qui croise inutilement des tables peut bloquer tout un écran. Deux réflexes économisent beaucoup de ressources : ne joindre que les tables réellement nécessaires à la question posée, et s assurer que les colonnes de jointure (les clés étrangères comme client_id) sont indexées, ce qui transforme une recherche lente en accès quasi instantané. Le tutoriel sur les index détaille ce point. Enfin, sélectionnez uniquement les colonnes utiles plutôt que toutes celles des tables jointes : le volume transféré s en trouve réduit d autant.

✅ Récapitulatif

Vous savez maintenant relier des tables : la jointure interne pour les correspondances, la jointure externe gauche pour conserver toutes les lignes d un côté, le filtrage sur NULL pour détecter les absences, l enchaînement de plusieurs jointures pour traverser le schéma, et l auto-jointure pour les hiérarchies. Ces techniques transforment des tables isolées en réponses complètes. L étape suivante consiste à résumer ces données reliées avec l agrégation.

🧾 Aide-mémoire

Élément Rôle
JOIN … ON Jointure interne (correspondances uniquement)
LEFT JOIN … ON Garder toutes les lignes de gauche
table AS alias Raccourcir et lever l ambiguïté
WHERE droite.id IS NULL Détecter les absences de correspondance
JOIN … JOIN … Enchaîner plusieurs tables
self JOIN (2 alias) Relier une table à elle-même

💪 À vous de jouer

1. Affichez chaque commande validée avec le nom du client et sa ville.
2. Listez les produits qui n ont jamais été commandés.
3. Affichez chaque vendeur avec le nom de son responsable direct.

Voir une solution
-- 1
SELECT cmd.id, c.nom, c.ville
FROM commandes AS cmd
JOIN clients AS c ON c.id = cmd.client_id
WHERE cmd.statut = 'validee';

-- 2
SELECT p.nom
FROM produits AS p
LEFT JOIN lignes_commande AS lc ON lc.produit_id = p.id
WHERE lc.id IS NULL;

-- 3
SELECT e.nom AS vendeur, m.nom AS responsable
FROM employes AS e
JOIN employes AS m ON m.id = e.manager_id
WHERE e.poste LIKE 'Vend%';

Tutoriels associés

Pour aller plus loin

FAQ

INNER ou LEFT JOIN, comment choisir ? Demandez-vous si vous voulez garder les lignes sans correspondance. Si oui, LEFT JOIN ; si vous ne voulez que les paires existantes, INNER JOIN.

Pourquoi ma LEFT JOIN se comporte comme une INNER JOIN ? Presque toujours parce qu une condition sur la table de droite figure dans le WHERE : elle élimine les lignes à NULL. Déplacez cette condition dans la clause ON.

Comment éviter les doublons après une jointure ? Les doublons viennent des relations un-à-plusieurs. Selon le besoin, on agrège avec GROUP BY ou on précise davantage les conditions de jointure.

Peut-on joindre sur autre chose qu une égalité ? Oui, la condition ON accepte n importe quelle expression booléenne, y compris des inégalités, mais l égalité de clés reste de loin le cas le plus courant.

Mots-clés : jointure SQL, INNER JOIN, LEFT JOIN, auto-jointure, combiner des tables, clé étrangère, relation.

مشاركة