Pour interroger efficacement des données réparties sur plsuieurs tables dans une base de données relationnelle, les requêtes de jointure et les sous-requêtes sont des outils fondamentaux. Cet article explique leur utilisation avec des exemples concrets en SQL.
Préparation des données d'exemple
Commençons par créer et peupler deux tables liées : une table classes et une table eleves.
CREATE DATABASE db_exemple;
USE db_exemple;
CREATE TABLE classes (
id_classe INT PRIMARY KEY AUTO_INCREMENT,
nom_classe VARCHAR(40) NOT NULL UNIQUE,
description VARCHAR(200)
);
CREATE TABLE eleves (
matricule CHAR(8) PRIMARY KEY,
prenom VARCHAR(20) NOT NULL,
genre CHAR(2) NOT NULL,
age INT NOT NULL,
ref_classe INT,
CONSTRAINT fk_eleves_classes FOREIGN KEY (ref_classe)
REFERENCES classes(id_classe)
ON UPDATE CASCADE ON DELETE CASCADE
);
Insérons quelques enregistrements dans ces tables.
INSERT INTO classes (nom_classe, description) VALUES ('Informatique A', 'Cours de programmation Java');
INSERT INTO classes (nom_classe, description) VALUES ('Informatique B', 'Cours de base de données');
INSERT INTO classes (nom_classe, description) VALUES ('Mathématiques', 'Cours d''analyse');
INSERT INTO eleves (matricule, prenom, genre, age, ref_classe) VALUES ('E1001', 'Alice', 'F', 20, 1);
INSERT INTO eleves (matricule, prenom, genre, age, ref_classe) VALUES ('E1002', 'Bob', 'M', 21, 1);
INSERT INTO eleves (matricule, prenom, genre, age, ref_classe) VALUES ('E1003', 'Claire', 'F', 19, 2);
INSERT INTO eleves (matricule, prenom, genre, age, ref_classe) VALUES ('E1004', 'David', 'M', 22, 2);
INSERT INTO eleves (matricule, prenom, genre, age, ref_classe) VALUES ('E1005', 'Eva', 'F', 20, NULL);
INSERT INTO eleves (matricule, prenom, genre, age, ref_classe) VALUES ('E1006', 'Felix', 'M', 21, NULL);
Les jointures (JOIN)
Une jointure permet de combiner des enregistrements de deux ou plusieurs tables en se basant sur une colonne commune.
Jointure interne (INNER JOIN)
La jointure interne retourne uniquement les enregistrements qui ont une correspondance dans les deux tables.
-- Syntaxe générale
SELECT colonnes FROM table1 INNER JOIN table2 ON table1.colonne_commune = table2.colonne_commune;
-- Exemple : Liste des élèves avec leur classe
SELECT e.prenom, e.genre, e.age, c.nom_classe
FROM eleves e
INNER JOIN classes c ON e.ref_classe = c.id_classe;
Les élèves Eva et Felix n'apparaissent pas dans le résultat, car ils ne sont pas associés à une classe.
Jointure gauche (LEFT JOIN)
La jointure gauche retourne tous les enregistrements de la table de gauche et les enregistrements correspondants de la table de droite. S'il n'y a pas de correspondance, le résultat conteint des valeurs NULL pour les colonnes de la table de droite.
-- Syntaxe générale
SELECT colonnes FROM table_gauche LEFT JOIN table_droite ON table_gauche.col = table_droite.col;
-- Exemple : Tous les élèves, même ceux sans classe
SELECT e.prenom, c.nom_classe
FROM eleves e
LEFT JOIN classes c ON e.ref_classe = c.id_classe;
Cette requête retournera également Eva et Felix, avec la colonne nom_classe vide (NULL).
Jointure droite (RIGHT JOIN)
La jointure droite est l'inverse de la jointure gauche. Elle retourne tous les enregistrements de la table de droite et les correspondacnes de la table de gauche.
-- Exemple : Toutes les classes, même celles sans élèves
SELECT e.prenom, c.nom_classe
FROM eleves e
RIGHT JOIN classes c ON e.ref_classe = c.id_classe;
Cette requête listera toutes les classes. La classe 'Mathématiques' apparaîtra, mais la colonne prenom sera NULL car aucun élève n'y est inscrit.
Les sous-requêtes (subqueries)
Une sous-requête est une requête SELECT imbriquée dans une autre requête. Elle peut être utilisée dans les clauses WHERE, FROM, ou SELECT.
Sous-requête retournant une valeur unique
-- Trouver les élèves de la classe 'Informatique B'
SELECT * FROM eleves
WHERE ref_classe = (SELECT id_classe FROM classes WHERE nom_classe = 'Informatique B');
Sous-requête retournant un ensemble de valeurs
On utilise l'opérateur IN lorsque la sous-requête peut retourner plusieurs valeurs.
-- Trouver les élèves des classes dont le nom contient 'Informatique'
SELECT * FROM eleves
WHERE ref_classe IN (SELECT id_classe FROM classes WHERE nom_classe LIKE '%Informatique%');
Sous-requête comme table dérivée dans la clause FROM
On peut utiliser une sous-requête pour créer une table temporaire sur laquelle on applique une nouvelle requête.
-- Filtrer les résultats d'une première requête
SELECT * FROM (
SELECT e.prenom, e.genre, c.nom_classe
FROM eleves e
LEFT JOIN classes c ON e.ref_classe = c.id_classe
) AS donnees_combinees
WHERE genre = 'F';