Les jointures
Interroger plusieurs tables à la fois.
Après cette fiche, vous saurez :
- relier deux tables avec INNER JOIN
- conserver les lignes sans correspondance avec LEFT JOIN
- utiliser des alias de table
- joindre trois tables
1Le problème
SELECT titre, num_auteur FROM livres;
titre | num_auteur --------------------------+----------- Germinal | 1 L'Assommoir | 1 Le Chien jaune | 2
Un numéro d'auteur n'intéresse personne. On veut le nom. Mais il est dans l'autre table.
C'est le prix de la découpe en tables de la fiche 1 : les données sont bien rangées, mais éparpillées. La jointure les rassemble au moment de la lecture.
2INNER JOIN
SELECT livres.titre, auteurs.nom FROM livres INNER JOIN auteurs ON livres.num_auteur = auteurs.num_auteur;
titre | nom --------------------------+--------------- Germinal | Zola L'Assommoir | Zola Le Chien jaune | Simenon Stupeur et tremblements | Nothomb Le Petit Prince | Saint-Exupéry
| Morceau | Rôle |
|---|---|
FROM livres | La première table |
INNER JOIN auteurs | La table qu'on y ajoute |
ON … | Comment les deux se correspondent |
À retenir
La condition ON est presque toujours la même : la clé étrangère d'un côté, la clé primaire de l'autre.
ON livres.num_auteur = auteurs.num_auteur
C'est exactement le lien qu'on a dessiné à la fiche 3, et déclaré à la fiche 7. Le travail d'analyse porte ses fruits ici : si vous savez où sont vos clés étrangères, vous savez écrire vos jointures.
Préfixer les colonnes
Quand deux tables ont une colonne du même nom, il faut préciser laquelle. On écrit
table.colonne.
Attention
Sans préfixe, PostgreSQL refuse.
SELECT titre, nom FROM livres INNER JOIN auteurs ON num_auteur = num_auteur;
ERROR: column reference "num_auteur" is ambiguous
Les deux tables ont une colonne num_auteur. PostgreSQL ne peut pas
deviner.
3Les alias : raccourcir
SELECT l.titre, a.nom, a.prenom FROM livres l INNER JOIN auteurs a ON l.num_auteur = a.num_auteur;
livres l donne le surnom l à la table. On peut aussi
écrire livres AS l, c'est identique.
Sur une jointure à trois tables, les alias font gagner énormément en lisibilité. Prenez l'habitude tout de suite.
4LEFT JOIN : garder tout le monde
INNER JOIN ne garde que les lignes qui ont une correspondance
des deux côtés. Parfois, ce n'est pas ce qu'on veut.
Question : quels sont tous les auteurs, avec leurs livres ?
INSERT INTO auteurs (nom, prenom, nationalite)
VALUES ('Hugo', 'Victor', 'Française');
SELECT a.nom, l.titre
FROM auteurs a
INNER JOIN livres l ON a.num_auteur = l.num_auteur;Victor Hugo n'apparaît pas. Il n'a aucun livre dans la base, donc aucune
correspondance, donc INNER JOIN l'écarte.
SELECT a.nom, l.titre FROM auteurs a LEFT JOIN livres l ON a.num_auteur = l.num_auteur;
nom | titre ---------------+-------------------------- Zola | Germinal Zola | L'Assommoir Simenon | Le Chien jaune Nothomb | Stupeur et tremblements Saint-Exupéry | Le Petit Prince Hugo | NULL
Avec LEFT JOIN, Hugo apparaît, avec NULL dans la colonne
titre. C'est bien plus honnête : il existe dans la base.
| Jointure | Garde… |
|---|---|
INNER JOIN | seulement les lignes qui ont une correspondance des deux côtés |
LEFT JOIN | toutes les lignes de la table de gauche, complétées par NULL |
RIGHT JOIN | toutes les lignes de la table de droite |
FULL JOIN | toutes les lignes des deux tables |
À retenir
Comment choisir ? Posez-vous une seule question : est-ce que je veux voir les éléments qui n'ont rien ?
- « Les livres avec leur auteur » →
INNER JOIN. Un livre sans auteur n'a pas d'intérêt. - « Tous les auteurs, même ceux sans livre » →
LEFT JOIN. - « Tous les lecteurs, même ceux qui n'ont jamais emprunté » →
LEFT JOIN.
RIGHT JOIN fait la même chose que LEFT JOIN en inversant
l'ordre des tables. En pratique, on n'en écrit presque jamais : on préfère mettre la
table importante à gauche.
Trouver ce qui n'a rien
Une astuce très utile : combiner LEFT JOIN et IS NULL.
-- Les auteurs sans aucun livre SELECT a.nom, a.prenom FROM auteurs a LEFT JOIN livres l ON a.num_auteur = l.num_auteur WHERE l.num_livre IS NULL;
nom | prenom ------+-------- Hugo | Victor
On garde tout le monde, puis on ne conserve que ceux dont le côté droit est vide.
5Joindre trois tables
Question : qui a emprunté quoi ? La réponse traverse trois tables, comme prévu par le n-n de la fiche 3.
SELECT lec.nom AS lecteur,
liv.titre AS livre,
e.date_emprunt
FROM emprunts e
INNER JOIN lecteurs lec ON e.num_lecteur = lec.num_lecteur
INNER JOIN livres liv ON e.num_livre = liv.num_livre
ORDER BY e.date_emprunt;lecteur | livre | date_emprunt --------+------------------+------------- Benali | Le Petit Prince | 2026-08-15 Dupuis | Germinal | 2026-09-01 Dupuis | Le Chien jaune | 2026-09-05 Martin | L'Assommoir | 2026-09-10
À retenir
La méthode, quel que soit le nombre de tables :
- Partez de la table du milieu — celle qui porte les clés étrangères.
- Ajoutez un
INNER JOINpar table à rattacher. - Chaque
ONrelie une clé étrangère à sa clé primaire.
Ici emprunts est au milieu : elle pointe vers lecteurs
et vers livres. C'est toujours la table de liaison qui sert de point de
départ.
On peut évidemment ajouter un filtre et remonter jusqu'à l'auteur :
SELECT lec.nom AS lecteur,
liv.titre AS livre,
aut.nom AS auteur,
e.date_prevue
FROM emprunts e
INNER JOIN lecteurs lec ON e.num_lecteur = lec.num_lecteur
INNER JOIN livres liv ON e.num_livre = liv.num_livre
INNER JOIN auteurs aut ON liv.num_auteur = aut.num_auteur
WHERE e.date_retour IS NULL
ORDER BY e.date_prevue;Quatre tables, et la requête reste lisible grâce aux alias.
→Exercices
Ajoutez d'abord un auteur sans livre et un lecteur sans emprunt.
INSERT INTO auteurs (nom, prenom, nationalite)
VALUES ('Hugo', 'Victor', 'Française');
INSERT INTO lecteurs (nom, prenom, telephone)
VALUES ('Petit', 'Lucie', '0470 99 88 77');Puis exécutez ces deux requêtes et comparez le nombre de lignes.
-- A SELECT a.nom, l.titre FROM auteurs a INNER JOIN livres l ON a.num_auteur = l.num_auteur; -- B SELECT a.nom, l.titre FROM auteurs a LEFT JOIN livres l ON a.num_auteur = l.num_auteur;
Solution
A : 5 lignes. Les cinq livres, chacun avec son auteur. Hugo est absent.
B : 6 lignes. Les cinq mêmes, plus Hugo avec NULL.
Pourquoi Zola apparaît-il deux fois ? Parce qu'il a deux livres. Une jointure produit une ligne par correspondance, pas une ligne par auteur.
C'est normal, et c'est important à comprendre : le résultat d'une jointure n'a pas le même nombre de lignes que la table de départ. Nous verrons à la fiche 11 comment regrouper pour compter.
Et si vous vouliez « tous les lecteurs, même sans emprunt » ?
SELECT lec.nom, e.date_emprunt FROM lecteurs lec LEFT JOIN emprunts e ON lec.num_lecteur = e.num_lecteur;
Lucie Petit apparaîtra avec NULL.
Écrivez la requête qui répond à chaque question.
- Le titre de chaque livre avec le nom et le prénom de son auteur.
- Les livres belges (dont l'auteur est belge).
- Tous les lecteurs, avec le nombre de jours depuis leur dernier emprunt — y compris ceux qui n'ont jamais emprunté.
- Les emprunts en retard, avec le nom du lecteur, le titre du livre et le nombre de jours de retard.
- Les livres qui n'ont jamais été empruntés.
Solution
-- 1 SELECT l.titre, a.nom, a.prenom FROM livres l INNER JOIN auteurs a ON l.num_auteur = a.num_auteur ORDER BY a.nom; -- 2 SELECT l.titre, a.nom FROM livres l INNER JOIN auteurs a ON l.num_auteur = a.num_auteur WHERE a.nationalite = 'Belge'; -- 3 SELECT lec.nom, lec.prenom, e.date_emprunt, CURRENT_DATE - e.date_emprunt AS jours FROM lecteurs lec LEFT JOIN emprunts e ON lec.num_lecteur = e.num_lecteur ORDER BY lec.nom; -- 4 SELECT lec.nom AS lecteur, liv.titre AS livre, e.date_prevue, CURRENT_DATE - e.date_prevue AS jours_de_retard FROM emprunts e INNER JOIN lecteurs lec ON e.num_lecteur = lec.num_lecteur INNER JOIN livres liv ON e.num_livre = liv.num_livre WHERE e.date_retour IS NULL AND e.date_prevue < CURRENT_DATE ORDER BY jours_de_retard DESC; -- 5 SELECT liv.titre FROM livres liv LEFT JOIN emprunts e ON liv.num_livre = e.num_livre WHERE e.num_emprunt IS NULL;
Sur la 3 : LEFT JOIN est indispensable. Avec
INNER JOIN, Lucie Petit disparaîtrait, alors que l'énoncé demande
explicitement « y compris ceux qui n'ont jamais emprunté ».
Sur la 4 : c'est la requête la plus utile de la fiche. Elle deviendra l'état
de sortie de la fiche 12. Remarquez qu'on peut trier sur
jours_de_retard, l'alias défini plus haut — PostgreSQL l'accepte
dans le ORDER BY.
Sur la 5 : c'est le motif « LEFT JOIN + IS NULL » de la section 4. Retenez-le, il répond à toute question de la forme « qui n'a pas de… ».
Sur votre propre base.
- Écrivez une jointure entre deux tables.
- Écrivez une jointure entre trois tables.
- Écrivez une requête qui trouve les éléments « sans rien » (LEFT JOIN + IS NULL).
- Pour chacune, écrivez la question en français en commentaire.
Solution
-- Quelles consultations, avec le nom du patient ?
SELECT c.date_consult, c.motif, p.nom, p.prenom
FROM consultations c
INNER JOIN patients p ON c.num_patient = p.num_patient
ORDER BY c.date_consult DESC;
-- Qui a consulté qui, et pour quel montant ?
SELECT c.date_consult,
p.nom AS patient,
m.nom AS medecin,
m.specialite,
c.montant
FROM consultations c
INNER JOIN patients p ON c.num_patient = p.num_patient
INNER JOIN medecins m ON c.num_medecin = m.num_medecin
ORDER BY c.date_consult DESC;
-- Quels patients ne sont jamais venus en consultation ?
SELECT p.nom, p.prenom, p.telephone
FROM patients p
LEFT JOIN consultations c ON p.num_patient = c.num_patient
WHERE c.num_consultation IS NULL;Les trois points à vérifier dans votre travail :
- Chaque
ONrelie bien une clé étrangère à une clé primaire. Si vous joignez sur autre chose, c'est probablement une erreur. - Vous avez utilisé des alias de table. Sur trois tables, c'est indispensable.
- Pour la troisième requête, vous testez
IS NULLsur une colonne de la table de droite — idéalement sa clé primaire, qui n'est jamais vide autrement.
Une erreur fréquente dans la requête 3 : tester
WHERE c.motif IS NULL. Ça ne marche pas, parce qu'une consultation peut
exister avec un motif vide. Il faut tester une colonne qui ne peut être
NULL que par absence de correspondance : la clé primaire.
Je sais faire
- Je sais écrire un INNER JOIN entre deux tables.
- Je sais quand utiliser LEFT JOIN plutôt qu'INNER JOIN.
- Je sais utiliser des alias pour alléger mes requêtes.
- Je sais joindre trois tables.
Une case qui coince ? Relisez la partie concernée avant de passer à la fiche suivante.