🧚 Écoute ! Beekeeper Studio est une interface de base de données rapide, moderne et open source Télécharger
2 juillet 2025 Par Matthew Rathbone

Les JOIN SQL combinent les lignes de deux tables en faisant correspondre une colonne qu’elles partagent, comme le author_id d’un livre et l’id d’un auteur. Le type de jointure décide du sort des lignes sans correspondance. Les exemples fonctionnent sous PostgreSQL, MySQL, SQLite et SQL Server.

Comment utiliser les JOIN SQL

Place la deuxième table après JOIN, et indique dans ON comment ses lignes correspondent à celles de la première :

SELECT columns
FROM table_a
JOIN table_b ON table_a.key = table_b.key;

Tous les exemples de cette section joignent les deux mêmes tables : Butler a écrit deux des livres, Asimov aucun, et personne ne sait qui a écrit Beowulf.

INNER JOIN

Diagramme d’INNER JOIN

Un INNER JOIN ne renvoie que les lignes qui correspondent dans les deux tables. JOIN seul équivaut à INNER JOIN.

-- Input: authors
-- id | name
-- ---+--------
-- 1  | Le Guin
-- 2  | Butler
-- 3  | Asimov

-- Input: books
-- title                | author_id
-- ---------------------+----------
-- The Dispossessed     | 1
-- Kindred              | 2
-- Parable of the Sower | 2
-- Beowulf              | NULL

SELECT a.name, b.title
FROM authors a
INNER JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower

Asimov n’a aucun livre et Beowulf n’a pas d’auteur, donc aucun des deux n’apparaît.

LEFT JOIN

Diagramme de LEFT JOIN

Un LEFT JOIN (ou LEFT OUTER JOIN) renvoie toutes les lignes de la table de gauche, celle qui suit FROM. Quand la table de droite n’a pas de correspondance, ses colonnes valent NULL.

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower
-- Asimov  | NULL

RIGHT JOIN

Diagramme de RIGHT JOIN

Un RIGHT JOIN (ou RIGHT OUTER JOIN) en est le miroir : toutes les lignes de la table de droite, avec des NULL là où la table de gauche n’a pas de correspondance.

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a
RIGHT JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower
-- NULL    | Beowulf

FULL OUTER JOIN

Diagramme de FULL OUTER JOIN

Un FULL OUTER JOIN renvoie toutes les lignes des deux tables, associées quand c’est possible et complétées par des NULL sinon. MySQL ne le propose pas : vois « Syntaxe alternative » plus bas.

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a
FULL OUTER JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower
-- NULL    | Beowulf
-- Asimov  | NULL

CROSS JOIN

Diagramme de CROSS JOIN

Un CROSS JOIN associe chaque ligne d’une table à chaque ligne de l’autre, il n’a donc pas de ON. Deux tailles et deux couleurs donnent quatre combinaisons ; 1000 lignes par 1000 lignes en donnent un million, alors réserve-le aux petites tables.

-- Input: sizes
-- size
-- ----
-- S
-- M

-- Input: colors
-- color
-- -----
-- Red
-- Blue

SELECT s.size, c.color
FROM sizes s
CROSS JOIN colors c;

-- Output:
-- size | color
-- -----+------
-- S    | Red
-- S    | Blue
-- M    | Red
-- M    | Blue

LEFT ANTI JOIN

Diagramme de LEFT ANTI JOIN

Un anti-join gauche renvoie les lignes de la table de gauche qui n’ont aucune correspondance dans la table de droite. Il n’existe pas de mot-clé pour ça : fais un LEFT JOIN et garde les lignes où le côté droit vaut NULL.

-- Input: the authors and books tables above

SELECT a.name
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE b.author_id IS NULL;

-- Output:
-- name
-- ------
-- Asimov

RIGHT ANTI JOIN

Diagramme de RIGHT ANTI JOIN

Un anti-join droit renvoie les lignes de la table de droite qui n’ont aucune correspondance dans la table de gauche.

-- Input: the authors and books tables above

SELECT b.title
FROM authors a
RIGHT JOIN books b ON a.id = b.author_id
WHERE a.id IS NULL;

-- Output:
-- title
-- -------
-- Beowulf

Règles à connaître

  • NULL ne correspond jamais à rien, pas même à un autre NULL. Le author_id NULL de Beowulf ne correspond à aucun auteur, donc seules les jointures qui gardent les livres sans correspondance le renvoient.
  • Une ligne apparaît une fois pour chaque ligne à laquelle elle correspond. Butler apparaît deux fois plus haut car elle correspond à deux livres. Si tu additionnes ensuite une colonne de la table des auteurs, ses valeurs comptent double.
  • Avec une jointure externe, une condition sur l’autre table se place dans ON. Dans WHERE, elle s’exécute après la jointure et élimine les lignes NULL que la jointure venait de garder :
-- Input: the authors and books tables above

-- Only Butler is left: the WHERE drops every author whose title is NULL
SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE b.title LIKE 'K%';

-- Output:
-- name   | title
-- -------+--------
-- Butler | Kindred

-- Every author is kept, with a book only where it starts with K
SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id AND b.title LIKE 'K%';

-- Output:
-- name    | title
-- --------+--------
-- Le Guin | NULL
-- Butler  | Kindred
-- Asimov  | NULL

Syntaxe alternative

MySQL n’a pas de FULL OUTER JOIN. Combine plutôt un LEFT JOIN avec les lignes sans correspondance d’un RIGHT JOIN :

-- MySQL
-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a LEFT JOIN books b ON a.id = b.author_id
UNION ALL
SELECT a.name, b.title
FROM authors a RIGHT JOIN books b ON a.id = b.author_id
WHERE a.id IS NULL;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Parable of the Sower
-- Butler  | Kindred
-- Asimov  | NULL
-- NULL    | Beowulf

Avant la version 3.39, SQLite n’a ni RIGHT JOIN ni FULL OUTER JOIN : inverse les tables et utilise un LEFT JOIN.

Un anti-join peut aussi s’écrire avec NOT EXISTS. Évite NOT IN pour ça : si la sous-requête renvoie un seul NULL, comme le author_id de Beowulf ici, NOT IN ne renvoie absolument rien :

-- Input: the authors and books tables above

SELECT a.name
FROM authors a
WHERE NOT EXISTS (SELECT 1 FROM books b WHERE b.author_id = a.id);

-- Output:
-- name
-- ------
-- Asimov

SELECT a.name
FROM authors a
WHERE a.id NOT IN (SELECT author_id FROM books);

-- Output:
-- name
-- ----
-- (0 rows)

Quand les colonnes de correspondance portent le même nom dans les deux tables, tu peux écrire JOIN books USING (author_id) à la place de la condition ON, sauf dans SQL Server.

Le code ancien liste souvent les tables séparées par des virgules et place la correspondance dans WHERE. C’est un INNER JOIN écrit autrement. Sans le WHERE, ça devient un CROSS JOIN :

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a, books b
WHERE a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower

Quand utiliser les JOIN SQL ?

Utilise une jointure dès que les données dont tu as besoin sont réparties sur plusieurs tables. Le choix de la jointure dépend du sort que tu réserves aux lignes sans correspondance, et la plupart des cas entrent dans l’une de ces cinq catégories.

Une ligne d’employé contient un ID de service, mais le rapport a besoin du nom du service. Un INNER JOIN va le chercher, et écarte toute ligne qui n’a rien à récupérer.

Garder les lignes sans correspondance

Quand les données liées sont facultatives, par exemple un employé qui n’a pas encore de service, un INNER JOIN le masquerait discrètement. Un LEFT JOIN garde toutes les lignes de la table principale et comble les trous avec NULL.

Lister chaque élément, même les vides

Les rapports ont souvent besoin de chaque service, catégorie ou client, y compris ceux qui ne contiennent encore rien. Fais de cette table celle dont toutes les lignes sont conservées : en premier avec un LEFT JOIN, ou en second avec un RIGHT JOIN.

Trouver les lignes sans correspondance

Clients qui n’ont jamais commandé, produits jamais vendus, employés sans service : un anti-join trouve ce qui manque, ce qui en fait aussi un bon contrôle de qualité des données.

Générer toutes les combinaisons

Un CROSS JOIN génère des lignes qui n’existent pas encore : chaque taille dans chaque couleur, chaque employé face à chaque formation, ou chaque magasin pour chaque jour d’un rapport.

Exemples concrets de JOIN SQL

Les étudiants et leurs cours

Une relation plusieurs-à-plusieurs nécessite deux INNER JOIN, via la table de liaison qui relie les étudiants aux cours. Mei n’est inscrite à rien, elle n’apparaît donc pas :

-- Input: students
-- id | name
-- ---+------
-- 1  | Amara
-- 2  | Lucas
-- 3  | Mei

-- Input: enrollments
-- student_id | course_id | grade
-- -----------+-----------+------
-- 1          | 10        | A
-- 1          | 20        | B
-- 2          | 10        | B

-- Input: courses
-- id | course_name
-- ---+------------
-- 10 | Databases
-- 20 | Statistics

SELECT s.name, c.course_name, e.grade
FROM students s
INNER JOIN enrollments e ON s.id = e.student_id
INNER JOIN courses c ON e.course_id = c.id;

-- Output:
-- name  | course_name | grade
-- ------+-------------+------
-- Amara | Databases   | A
-- Amara | Statistics  | B
-- Lucas | Databases   | B

Utilisateurs avec profil facultatif

Tous les utilisateurs n’ont pas rempli leur profil. Un LEFT JOIN les garde tous :

-- Input: users
-- id | username
-- ---+---------
-- 1  | rosa_m
-- 2  | devon
-- 3  | kai_l

-- Input: user_profiles
-- user_id | bio
-- --------+------------------------
-- 1       | Data engineer in Lisbon
-- 3       | Loves SQL puzzles

SELECT u.username, p.bio
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id;

-- Output:
-- username | bio
-- ---------+------------------------
-- rosa_m   | Data engineer in Lisbon
-- devon    | NULL
-- kai_l    | Loves SQL puzzles

Nombre de commandes par client

Pour inclure les clients sans commande, fais un LEFT JOIN depuis customers. Compte ensuite une colonne de la table des commandes : COUNT(*) compte la ligne créée par le LEFT JOIN pour Omar, qui obtiendrait alors 1 au lieu de 0 :

-- Input: customers
-- id | name
-- ---+------------
-- 1  | Harper Lee
-- 2  | Omar Haddad
-- 3  | Yuki Tanaka

-- Input: orders
-- id  | customer_id | total
-- ----+-------------+-------
-- 101 | 1           | 59.90
-- 102 | 3           | 120.00
-- 103 | 1           | 14.50

-- Wrong: COUNT(*) counts Omar's NULL row
SELECT c.name, COUNT(*) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY c.id;

-- Output:
-- name        | order_count
-- ------------+------------
-- Harper Lee  | 2
-- Omar Haddad | 1
-- Yuki Tanaka | 1

-- Right: COUNT(o.id) skips it
SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY c.id;

-- Output:
-- name        | order_count
-- ------------+------------
-- Harper Lee  | 2
-- Omar Haddad | 0
-- Yuki Tanaka | 1

Factures et paiements qui ne concordent pas

Un FULL OUTER JOIN qui ne garde que les lignes sans correspondance compare deux listes en une seule requête : la facture 502 n’a pas été payée, et le paiement 9003 concerne une facture qui n’existe pas :

-- Input: invoices
-- invoice_id | amount
-- -----------+-------
-- 501        | 120.00
-- 502        | 75.50
-- 503        | 300.00

-- Input: payments
-- payment_id | invoice_id | paid
-- -----------+------------+-------
-- 9001       | 501        | 120.00
-- 9002       | 503        | 300.00
-- 9003       | 504        | 42.00

-- MySQL has no FULL OUTER JOIN: use the UNION ALL form under "Alternative syntax"
SELECT i.invoice_id, i.amount, p.payment_id, p.invoice_id AS paid_invoice
FROM invoices i
FULL OUTER JOIN payments p ON p.invoice_id = i.invoice_id
WHERE i.invoice_id IS NULL OR p.payment_id IS NULL;

-- Output:
-- invoice_id | amount | payment_id | paid_invoice
-- -----------+--------+------------+-------------
-- 502        | 75.50  | NULL       | NULL
-- NULL       | NULL   | 9003       | 504

Recettes quotidiennes de chaque magasin, y compris les jours sans vente

Fais un CROSS JOIN entre les magasins et les jours pour obtenir une ligne par magasin et par jour, puis un LEFT JOIN des ventes sur cette grille. York n’a rien vendu le 6 octobre, et a quand même sa ligne :

-- Input: stores
-- store
-- -----
-- Leeds
-- York

-- Input: days
-- day
-- ----------
-- 2026-10-05
-- 2026-10-06

-- Input: sales
-- store | day        | amount
-- ------+------------+-------
-- Leeds | 2026-10-05 | 410.00
-- Leeds | 2026-10-06 | 385.50
-- York  | 2026-10-05 | 220.00

SELECT st.store, d.day, COALESCE(SUM(s.amount), 0.00) AS takings
FROM stores st
CROSS JOIN days d
LEFT JOIN sales s ON s.store = st.store AND s.day = d.day
GROUP BY st.store, d.day
ORDER BY st.store, d.day;

-- Output:
-- store | day        | takings
-- ------+------------+--------
-- Leeds | 2026-10-05 | 410.00
-- Leeds | 2026-10-06 | 385.50
-- York  | 2026-10-05 | 220.00
-- York  | 2026-10-06 | 0.00

Pour aller plus loin sur les JOIN SQL

Sur ce blog :

Documentation officielle :

Beekeeper Studio est une interface de base de données gratuite & open source

Le meilleur outil de requêtes & éditeur SQL que j’aie jamais utilisé. Il m’offre tout ce dont j’ai besoin pour gérer ma base de données. - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studio est rapide, intuitif et facile à utiliser. Beekeeper prend en charge plein de bases de données et fonctionne parfaitement sous Windows, Mac et Linux.

La version Linux de Beekeeper est 100% complète : rien de bâclé, aucun compromis sur les fonctionnalités.

Ce que les utilisateurs disent de Beekeeper Studio

★★★★★
« Beekeeper Studio a complètement remplacé mon ancien workflow SQL. C’est rapide, intuitif, et ça rend le travail sur les bases de données à nouveau agréable. »
— Alex K., développeur de bases de données
★★★★★
« J’ai essayé beaucoup d’interfaces de bases de données, mais Beekeeper trouve l’équilibre parfait entre fonctionnalités et simplicité. Ça marche, tout simplement. »
— Sarah M., ingénieure full stack