Mission Carthage

Accueil › Cours › SQL : jointures, regroupements et sous-requêtes

SQL : jointures, GROUP BY, HAVING et sous-requêtes

Chapitre 29 · Bac SI

Relie plusieurs tables dans une requête, compte et calcule des totaux par groupe avec GROUP BY et HAVING, utilise une requête dans une autre, et calcule sur les dates.

Les leçons de ce chapitre

  1. Les jointures
  2. Compter et regrouper
  3. Sous-requêtes et dates

Leçon 29.1 : Les jointures

12 min

Une jointure réunit les lignes de deux tables qui vont ensemble : chaque élève avec sa classe. On l'écrit avec JOIN et la condition qui les relie après ON : la clé étrangère est égale à la clé primaire.

SELECT eleve.nom, classe.niveau, classe.salle
FROM eleve JOIN classe ON eleve.code_classe = classe.code;

Attention : Quand deux tables ont une colonne du même nom, MySQL ne sait pas laquelle prendre : erreur #1052 (« ambiguous »). Écris table.colonne ou alias.colonne.

En SQL

Exécute, puis ajoute WHERE c.niveau = 'Bac SI' avant le ORDER BY pour ne garder que les élèves du bac.

CREATE TABLE classe (code VARCHAR(5) PRIMARY KEY, niveau VARCHAR(20) NOT NULL, salle VARCHAR(5));
CREATE TABLE eleve (
  id INT PRIMARY KEY,
  nom VARCHAR(30) NOT NULL,
  code_classe VARCHAR(5),
  moyenne DECIMAL(4,2),
  date_naissance DATE,
  FOREIGN KEY (code_classe) REFERENCES classe(code)
);
INSERT INTO classe VALUES ('4SI1', 'Bac SI', 'B12'), ('4SI2', 'Bac SI', 'B14'), ('3SI1', '3ème SI', 'A03');
INSERT INTO eleve VALUES
  (1, 'Ben Salah', '4SI1', 12.50, '2008-03-14'),
  (2, 'Trabelsi', '4SI1', 16.25, '2008-07-02'),
  (3, 'Jaziri', '4SI2', 9.75, '2008-11-23'),
  (4, 'Mansour', '4SI2', 14.00, '2007-12-30'),
  (5, 'Bouzid', '3SI1', 11.00, '2009-05-05');

SELECT e.nom, c.niveau, c.salle
FROM eleve e JOIN classe c ON e.code_classe = c.code
ORDER BY e.nom;

Leçon 29.2 : Compter et regrouper

12 min

Les fonctions d'agrégation

SELECT COUNT(*) AS nb, AVG(moyenne) AS moy_generale, MAX(moyenne) AS meilleure FROM eleve;

GROUP BY et HAVING

GROUP BY coupe les lignes en groupes qui ont la même valeur, et les fonctions d'agrégation calculent un résultat par groupe. HAVING filtre ensuite les groupes, comme WHERE filtre les lignes.

SELECT code_classe, COUNT(*) AS nb, AVG(moyenne) AS moy
FROM eleve
GROUP BY code_classe
HAVING COUNT(*) >= 2;

Attention : Une colonne du SELECT qui n'est pas une fonction d'agrégation doit être dans le GROUP BY. Et une condition sur un COUNT ou un SUM va dans HAVING, jamais dans WHERE (erreur #1111).

Astuce : L'ordre d'écriture est toujours : SELECT … FROM … JOIN … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT ….

En SQL

Exécute, puis change HAVING COUNT(*) >= 2 en HAVING AVG(moyenne) >= 12 : quelles classes restent ?

CREATE TABLE classe (code VARCHAR(5) PRIMARY KEY, niveau VARCHAR(20) NOT NULL, salle VARCHAR(5));
CREATE TABLE eleve (
  id INT PRIMARY KEY,
  nom VARCHAR(30) NOT NULL,
  code_classe VARCHAR(5),
  moyenne DECIMAL(4,2),
  date_naissance DATE,
  FOREIGN KEY (code_classe) REFERENCES classe(code)
);
INSERT INTO classe VALUES ('4SI1', 'Bac SI', 'B12'), ('4SI2', 'Bac SI', 'B14'), ('3SI1', '3ème SI', 'A03');
INSERT INTO eleve VALUES
  (1, 'Ben Salah', '4SI1', 12.50, '2008-03-14'),
  (2, 'Trabelsi', '4SI1', 16.25, '2008-07-02'),
  (3, 'Jaziri', '4SI2', 9.75, '2008-11-23'),
  (4, 'Mansour', '4SI2', 14.00, '2007-12-30'),
  (5, 'Bouzid', '3SI1', 11.00, '2009-05-05');

SELECT code_classe, COUNT(*) AS nb, AVG(moyenne) AS moy
FROM eleve
GROUP BY code_classe
HAVING COUNT(*) >= 2;

Leçon 29.3 : Sous-requêtes et dates

12 min

Une requête dans une requête

Une sous-requête est un SELECT entre parenthèses, placé dans le WHERE d'une autre requête. Elle est calculée d'abord, puis son résultat sert à la condition.

SELECT nom, moyenne FROM eleve
WHERE moyenne > (SELECT AVG(moyenne) FROM eleve);

SELECT nom FROM eleve
WHERE code_classe IN (SELECT code FROM classe WHERE niveau = 'Bac SI');

Calculer avec les dates

SELECT nom, YEAR(date_naissance) AS annee FROM eleve WHERE MONTH(date_naissance) >= 7;

En SQL

Exécute : la première requête garde les élèves au-dessus de la moyenne générale. Ajoute une requête qui affiche le nom et l'âge en jours de chaque élève au 1er octobre 2026 avec DATEDIFF('2026-10-01', date_naissance).

CREATE TABLE classe (code VARCHAR(5) PRIMARY KEY, niveau VARCHAR(20) NOT NULL, salle VARCHAR(5));
CREATE TABLE eleve (
  id INT PRIMARY KEY,
  nom VARCHAR(30) NOT NULL,
  code_classe VARCHAR(5),
  moyenne DECIMAL(4,2),
  date_naissance DATE,
  FOREIGN KEY (code_classe) REFERENCES classe(code)
);
INSERT INTO classe VALUES ('4SI1', 'Bac SI', 'B12'), ('4SI2', 'Bac SI', 'B14'), ('3SI1', '3ème SI', 'A03');
INSERT INTO eleve VALUES
  (1, 'Ben Salah', '4SI1', 12.50, '2008-03-14'),
  (2, 'Trabelsi', '4SI1', 16.25, '2008-07-02'),
  (3, 'Jaziri', '4SI2', 9.75, '2008-11-23'),
  (4, 'Mansour', '4SI2', 14.00, '2007-12-30'),
  (5, 'Bouzid', '3SI1', 11.00, '2009-05-05');

SELECT nom, moyenne FROM eleve
WHERE moyenne > (SELECT AVG(moyenne) FROM eleve);
SELECT nom FROM eleve
WHERE code_classe IN (SELECT code FROM classe WHERE niveau = 'Bac SI');

S'entraîner gratuitement

Chaque leçon a 3 exercices gratuits avec indices pour appliquer ce chapitre avec Hannibot.

Ouvrir le chapitre dans Mission Carthage Essayer dans le compilateur en ligne

← Chapitre précédent : Requêtes SQL simples · Chapitre suivant : PHP : premiers scripts →

Tous les chapitres · Voir les devoirs corrigés de 2ème TI

Cours écrit pour Mission Carthage d'après le programme officiel. Ton professeur reste la référence.