Installation

Les jointures SQL permettent de récupérer des données provenant de deux tables différentes. Il existe différents types de jointures à connaitre. Je propose ici un petit tuto basé sur un exemple plutôt que les traditionnels schéma qui trainent sur le net et qui n’aident pas à la compréhension. Pour bien comprendre les jointures, je vous propose de créer un petit environnement de test que je vous encourage vivement à reproduire. Il s’agit donc de deux tables :

  • category : une liste de catégories
  • movies : une liste de films

La suite reste simple : chaque film peut se ranger dans une ou zéro catégories. Chaque catégorie est identifiée avec sa clé primaire. On utilisera une clé étrangère (category_id) dans la table movies. Voici la requête pour créer les tables :

/* category table defintion */
CREATE TABLE `category` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

/* movies table defintion */
CREATE TABLE `movies` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  `category_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `category_id` (`category_id`),
  CONSTRAINT `movies_ibfk_3` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

/* category data */
INSERT INTO `category` (`id`, `name`) VALUES
(1, 'Fiction'),
(2, 'Love'),
(3, 'Adventure'),
(4, 'Action'),
(5, 'Comic');

/* movies data */
INSERT INTO `movies` (`id`, `name`, `category_id`) VALUES
(1,	'Avatar',	1),
(2,	'L\'agence',	2),
(3,	'Terminator',	NULL),
(4,	'Cars 3',	5),
(5,	'True Lies',	4),
(6,	'Hunger Games',	4),
(7,	'Expandable',	4);

Vous devriez donc vous retrouver avec quelque chose de similaire :

/* Table movies */
+----+--------------+-------------+
| id | name         | category_id |
+----+--------------+-------------+
| 1  | Avatar       | 1           |
+----+--------------+-------------+
| 2  | L'agence     | 2           |
+----+--------------+-------------+
| 3  | Terminator   | NULL        |
+----+--------------+-------------+
| 4  | Cars 3       | 5           |
+----+--------------+-------------+
| 5  | True Lies    | 4           |
+----+--------------+-------------+
| 6  | Hunger Games | 4           |
+----+--------------+-------------+
| 7  | Expandable   | 4           |
+----+--------------+-------------+

/* Table category */
+----+-----------+
| id | name      |
+----+-----------+
| 1  | Fiction   |
+----+-----------+
| 2  | Love      |
+----+-----------+
| 3  | Adventure |
+----+-----------+
| 4  | Action    |
+----+-----------+
| 5  | Comic     |
+----+-----------+

Notez ceci : le film Terminator n’a pas de catégorie et il n’ y a aucuns films dans la catégorie Adventure. Scénario idéal pour nos jointures !

Les jointures

On peut désormais entrer dans le vif du sujet. Comprenez ceci : une jointure fonctionne avec deux tables A et B par exemple. Le schéma est toujours le même :

SELECT something FROM A JOIN B ON condition

A est la table de gauche et B la table de droite (c’est important pour la suite). Pour chaque ligne de A, le moteur cherche à vérifier la condition dans B. La condition porte sur une colonne de A et de B (ex : A.id = B.id). Il n’est pas nécessaire que ces colonnes aient le même nom.
Petite liste des jointures qu’on va étudier :

  • INNER JOIN : jointure par défaut lorsqu’on écrit simplement JOIN, elle permet de joindre deux lignes de tables différentes lorsque la condition est vrai des 2 côtés
  • LEFT JOIN : aussi notée LEFT OUTER JOIN, cette jointure permet de joindre deux lignes de tables différentes. Si la condition n’est pas respectée dans la table de droite, la ligne de la table de gauche est quand même insérée dans le jeu de résultat. En d’autres termes, cette jointure garantie la présence de toutes les lignes de la table de gauche
  • RIGHT JOIN : aussi notée RIGHT OUTER JOIN, cette jointure permet de joindre deux lignes de tables différentes. Si la condition n’est pas respectée dans la table de gauche, la ligne de la table de droite est quand même insérée dans le jeu de résultat. En d’autres termes, cette jointure garantie la présence de toutes les lignes de la table de droite
  • NATURAL JOIN : jointure qui permet de joindre deux lignes de tables différentes si au moins 2 des colonnes portent le même nom

INNER JOIN

Commençons par la jointure la plus simple : nous allons joindre nos deux tables à l’aide de la clé étrangère category_id qui est bien sûr l’id de la catégorie. La condition est donc category.id = movies.category_id :

SELECT * FROM `movies` m
JOIN `category` c
   ON (c.id = m.category_id);

Regardez la colonne category_id et id !! Les deux mêmes, bien sûr, car il s’agit de la condition. Remarquez l’absence d’une ligne (film Terminator) qui lui, n’avait pas de catégorie (category_id = NULL). Aucune catégorie a un id qui est égal à NULL.

/* result */
+----+--------------+-------------+----+---------+
| id | name         | category_id | id | name    |
+----+--------------+-------------+----+---------+
| 1  | Avatar       | 1           | 1  | Fiction |
+----+--------------+-------------+----+---------+
| 2  | L'agence     | 2           | 2  | Love    |
+----+--------------+-------------+----+---------+
| 4  | Cars 3       | 5           | 5  | Comic   |
+----+--------------+-------------+----+---------+
| 5  | True Lies    | 4           | 4  | Action  |
+----+--------------+-------------+----+---------+
| 6  | Hunger Games | 4           | 4  | Action  |
+----+--------------+-------------+----+---------+
| 7  | Expandable   | 4           | 4  | Action  |
+----+--------------+-------------+----+---------+

Avec une jointure INNER JOIN, la condition doit être vérifiée dans les deux tables. Si la condition n’est pas valable dans la table de droite, la ligne de la table de gauche est retirée du jeu de résultat.

LEFT JOIN

Assez similaire à la jointure précédente à une exception près : quand la condition n’est pas respectée dans la table de droite, la ligne de la table de gauche est quand même insérée, les valeurs manquantes de la table de droite prennent la valeur NULL.

SELECT * FROM `movies` m
LEFT JOIN `category` c
   ON (c.id = m.category_id);

Avec un tout petit mot clé, la différence est énorme :

/* result */
+----+--------------+-------------+------+---------+
| id | name         | category_id | id   | name    |
+----+--------------+-------------+------+---------+
| 1  | Avatar       | 1           | 1    | Fiction |
+----+--------------+-------------+------+---------+
| 2  | L'agence     | 2           | 2    | Love    |
+----+--------------+-------------+------+---------+
| 3  | Terminator   | NULL        | NULL | NULL    |
+----+--------------+-------------+------+---------+
| 4  | Cars 3       | 5           | 5    | Comic   |
+----+--------------+-------------+------+---------+
| 5  | True Lies    | 4           | 4    | Action  |
+----+--------------+-------------+------+---------+
| 6  | Hunger Games | 4           | 4    | Action  |
+----+--------------+-------------+------+---------+
| 7  | Expandable   | 4           | 4    | Action  |
+----+--------------+-------------+------+---------+

Avec une jointure LEFT JOIN, lorsque la condition n’est pas vraie dans la table de droite, la ligne de la table de gauche est tout de même insérée dans le jeu de résultat. Les valeurs manquantes de la table de droite prennent la valeur NULL.

RIGHT JOIN

Similaire à la précédente : cette fois quand la condition n’est pas respectée dans la table de gauche, la ligne de la table de droite est quand même insérée, les valeurs manquantes de la table de gauche prennent alors la valeur NULL.

SELECT * FROM `movies` m
RIGHT JOIN `category` c
   ON (c.id = m.category_id);

Observez bien le résultat par rapport à juste avant :

/* result */
+------+--------------+-------------+----+-----------+
| id   | name         | category_id | id | name      |
+------+--------------+-------------+----+-----------+
| 1    | Avatar       | 1           | 1  | Fiction   |
+------+--------------+-------------+----+-----------+
| 2    | L'agence     | 2           | 2  | Love      |
+------+--------------+-------------+----+-----------+
| NULL | NULL         | NULL        | 3  | Adventure |
+------+--------------+-------------+----+-----------+
| 5    | True Lies    | 4           | 4  | Action    |
+------+--------------+-------------+----+-----------+
| 6    | Hunger Games | 4           | 4  | Action    |
+------+--------------+-------------+----+-----------+
| 7    | Expandable   | 4           | 4  | Action    |
+------+--------------+-------------+----+-----------+
| 4    | Cars 3       | 5           | 5  | Comic     |
+------+--------------+-------------+----+-----------+

Cette fois, le film Terminator n’est plus là mais la catégorie Adventure est apparue ! Normal, elle est dans la table de droite.

Avec une jointure RIGHT JOIN, lorsque la condition n’est pas vraie dans la table de gauche, la ligne de la table de droite est tout de même insérée dans le jeu de résultat. Les valeurs manquantes de la table de gauche prennent la valeur NULL.

NATURAL JOIN

Avec cette jointure, la condition est optionnelle la table de gauche et de droite doivent avoir une colonne avec le même nom et type. Pour illustrer ça, on va modifier notre table