SQL Avancé

JOIN, agrégation, GROUP BY et HAVING

Utilisez les flĂšches, cliquez ou glissez pour naviguer

Objectifs de la leçon

1. Croiser des données avec JOIN

Relier des tables entre elles

2. Choisir entre INNER et LEFT JOIN

Comprendre la différence clé

3. Agréger avec GROUP BY

COUNT, SUM, AVG, MIN, MAX

4. Filtrer les groupes avec HAVING

Pourquoi WHERE ne suffit pas

Plan du cours

1

Pourquoi JOIN ?

Croiser des données entre plusieurs tables

2

INNER JOIN

Le plus courant — seulement les correspondances

3

LEFT JOIN

Garder toutes les lignes de gauche mĂȘme sans correspondance

4

Fonctions d'agrégation & GROUP BY

COUNT, SUM, AVG, MIN, MAX — regrouper et calculer

5

HAVING & l'ordre d'exécution SQL

Filtrer les groupes et comprendre l'ordre

Module 1

Pourquoi JOIN ?

Croiser des données entre plusieurs tables

Les données sont éclatées dans plusieurs tables

Comment les rassembler ?

Le problÚme : des tables séparées

Chaque table stocke une seule entité

Mais souvent on veut combiner les informations

📋 Table articles

id | titre | categorie_id

1 | "PHP" | 1

2 | "SQL" | 1

3 | "CSS" | 2

4 | "JS" | NULL

📋 Table categories

id | nom

1 | "Backend"

2 | "Frontend"

3 | "DevOps"

💡 On veut afficher : "PHP — Backend", "CSS — Frontend"

Mais le nom de la catégorie n'est PAS dans la table articles !

Penser le JOIN visuellement

JOIN = deux tableaux cĂŽte Ă  cĂŽte qu'on colle

articles

id | titre | categorie_id

1 | PHP | 1

2 | SQL | 1

3 | CSS | 2

categories

id | nom

1 | Backend

2 | Frontend

3 | DevOps

On colle les lignes oĂč articles.categorie_id = categories.id

Résultat du JOIN

titre | categorie_id | nom

PHP | 1 | Backend

SQL | 1 | Backend

CSS | 2 | Frontend

Module 2

INNER JOIN

Seulement les correspondances

🟣

Table GAUCHE

Tous les articles

⭐

Zone commune

Les correspondances

đŸ””

Table DROITE

Toutes les catégories

INNER JOIN ne garde que la ⭐ zone commune

INNER JOIN — Syntaxe

SELECT articles.titre, categories.nom

FROM articles

INNER JOIN categories

ON articles.categorie_id = categories.id;

FROM articles

Table de départ (gauche)

INNER JOIN categories

Table Ă  coller (droite)

ON ... = ...

Condition de correspondance

⚠ Sans la clause ON → produit cartĂ©sien (catastrophe !)

Chaque ligne de gauche serait combinée avec chaque ligne de droite

INNER JOIN — Exemple rĂ©aliste

SELECT a.titre, c.nom AS categorie

FROM articles AS a

INNER JOIN categories AS c

ON a.categorie_id = c.id;

Résultat

titre | categorie

PHP | Backend

SQL | Backend

CSS | Frontend

❌ L'article "JS" (categorie_id = NULL) n'apparaüt pas

INNER JOIN exclut les lignes sans correspondance

❌ La catĂ©gorie "DevOps" (id=3) n'apparaĂźt pas non plus

Aucun article ne pointe vers elle

Module 3

LEFT JOIN

Garder TOUTES les lignes de gauche

đŸ””

Table GAUCHE

Tous les articles

← GardĂ©e ENTIERE

⭐

Correspondances

Données collées

🟣

Table DROITE

Catégories

LEFT JOIN garde TOUTE la table gauche — les absents reçoivent NULL

LEFT JOIN — Syntaxe

SELECT a.titre, c.nom AS categorie

FROM articles AS a

LEFT JOIN categories AS c

ON a.categorie_id = c.id;

Seul changement : INNER → LEFT

Le reste de la requĂȘte est identique !

💡 La table "gauche" = celle dans le FROM

💡 La table "droite" = celle dans le JOIN

LEFT JOIN — Exemple avec NULL

SELECT a.titre, c.nom AS categorie

FROM articles AS a

LEFT JOIN categories AS c

ON a.categorie_id = c.id;

Résultat

titre | categorie

PHP | Backend

SQL | Backend

CSS | Frontend

JS | NULL

✅ "JS" apparaĂźt dans le rĂ©sultat avec NULL pour la catĂ©gorie

LEFT JOIN garde TOUTE la table gauche, mĂȘme sans correspondance

INNER JOIN vs LEFT JOIN

INNER JOIN

Seulement les correspondances

✅ PHP → Backend

✅ SQL → Backend

✅ CSS → Frontend

❌ JS (pas de catĂ©gorie)

❌ DevOps (pas d'article)

LEFT JOIN

Toute la table gauche + correspondances

✅ PHP → Backend

✅ SQL → Backend

✅ CSS → Frontend

⚠ JS → NULL

❌ DevOps (pas d'article)

💡 Quand utiliser LEFT JOIN ?

Quand on veut TOUS les Ă©lĂ©ments de la table gauche, mĂȘme ceux sans correspondance

Quand choisir INNER vs LEFT ?

INNER JOIN

Je veux seulement les lignes qui ont une correspondance

‱ "Tous les articles qui ont une catĂ©gorie"

‱ "Les commandes avec un client valide"

‱ "Les Ă©tudiants inscrits Ă  un cours"

LEFT JOIN

Je veux absolument toutes les lignes de gauche

‱ "Tous les articles, mĂȘme sans catĂ©gorie"

‱ "Tous les clients, mĂȘme sans commande"

‱ "Tous les Ă©tudiants, mĂȘme non inscrits"

💡 En cas de doute, demandez-vous : "Est-ce que je veux les lignes sans correspondance ?"

Module 4

Fonctions d'agrégation

COUNT, SUM, AVG, MIN, MAX

Transformer plusieurs lignes en une seule valeur

Compter, additionner, moyenner...

COUNT — Compter les lignes

Combien d'articles dans la table ?

SELECT COUNT(*) AS total_articles

FROM articles;

Résultat

total_articles

4

COUNT(*) compte toutes les lignes

COUNT(categorie_id) compte les lignes oĂč categorie_id n'est pas NULL

SUM & AVG — Additionner et Moyenner

SUM — Somme totale

SELECT SUM(prix) AS total

FROM articles;

total

149.97

AVG — Moyenne

SELECT AVG(prix) AS moyenne

FROM articles;

moyenne

49.99

💡 SUM et AVG ne comptent pas les valeurs NULL

Si un article n'a pas de prix, il est ignoré du calcul

MIN & MAX — ExtrĂȘmes

MIN — Valeur minimale

SELECT MIN(prix) AS moins_cher

FROM articles;

moins_cher

29.99

MAX — Valeur maximale

SELECT MAX(prix) AS plus_cher

FROM articles;

plus_cher

69.99

MIN/MAX fonctionnent aussi sur du texte (ordre alphabétique) et des dates !

RĂ©capitulatif — Fonctions d'agrĂ©gation

COUNT

Nombre de lignes

SUM

Somme des valeurs

AVG

Moyenne des valeurs

MIN

Valeur minimale

MAX

Valeur maximale

⚠ Sans GROUP BY, ces fonctions travaillent sur toute la table

On a besoin de GROUP BY pour les calculer par groupe

Module 5

GROUP BY

Regrouper pour calculer par catégorie

Au lieu de compter tous les articles...

Compter les articles par catégorie

GROUP BY — Syntaxe

SELECT categorie_id, COUNT(*) AS nb_articles

FROM articles

GROUP BY categorie_id;

GROUP BY

Colonne pour regrouper

COUNT(*)

Calcul par groupe

AS nb_articles

Nom du résultat

⚠ Chaque colonne dans le SELECT doit ĂȘtre :

‱ Soit dans le GROUP BY

‱ Soit dans une fonction d'agrĂ©gation (COUNT, SUM...)

GROUP BY + JOIN — Exemple complet

Combien d'articles par catégorie ? Avec le nom de la catégorie !

SELECT c.nom AS categorie, COUNT(*) AS nb_articles

FROM articles AS a

INNER JOIN categories AS c

ON a.categorie_id = c.id

GROUP BY c.nom;

Résultat

categorie | nb_articles

Backend | 2

Frontend | 1

💡 On combine JOIN + GROUP BY pour regrouper sur une colonne d'une autre table

Module 6

HAVING

Filtrer les groupes — pourquoi WHERE ne suffit pas

WHERE filtre les lignes

HAVING filtre les groupes

Pourquoi WHERE ne marche pas ?

ProblÚme : "Les catégories qui ont plus de 3 articles"

❌ Ça ne marche PAS :

SELECT categorie_id, COUNT(*)

FROM articles

WHERE COUNT(*) > 3 ← ERREUR !

GROUP BY categorie_id;

⚠ WHERE s'exĂ©cute AVANT le GROUP BY

Au moment du WHERE, les groupes n'existent pas encore !

COUNT(*) n'a pas encore été calculé !

HAVING — La bonne syntaxe

✅ La bonne façon :

SELECT categorie_id, COUNT(*) AS nb

FROM articles

GROUP BY categorie_id

HAVING COUNT(*) > 3;

❌ WHERE COUNT(*) > 3

Les groupes n'existent pas encore

✅ HAVING COUNT(*) > 3

Les groupes sont formés, on peut filtrer

💡 HAVING s'exĂ©cute APRÈS le GROUP BY

HAVING — Exemple complet

Catégories qui ont plus de 1 article, avec le nom de la catégorie

SELECT c.nom, COUNT(*) AS nb

FROM articles AS a

INNER JOIN categories AS c ON a.categorie_id = c.id

GROUP BY c.nom

HAVING COUNT(*) > 1;

Résultat

nom | nb

Backend | 2

Frontend a seulement 1 article → exclu par HAVING

L'ordre d'exécution SQL

Pas l'ordre d'Ă©criture, mais l'ordre dans lequel SQL traite la requĂȘte !

1 FROM Quelle table ?
2 JOIN ... ON Coller les tables
3 WHERE Filtrer les lignes
4 GROUP BY Former les groupes
5 HAVING Filtrer les groupes

L'ordre d'exécution SQL (suite)

6 SELECT Choisir les colonnes + calculs
7 ORDER BY Trier les résultats
8 LIMIT Limiter le nombre de lignes

FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT

⚠ WHERE est exĂ©cutĂ© AVANT GROUP BY → il ne peut pas filtrer sur COUNT(*)

⚠ HAVING est exĂ©cutĂ© APRÈS GROUP BY → il peut filtrer sur COUNT(*)

❌ / ✅ Piùge n°1 : WHERE vs HAVING

❌ Faux

SELECT categorie_id, COUNT(*)

FROM articles

WHERE COUNT(*) > 3

GROUP BY categorie_id;

ERREUR ! WHERE ne peut pas utiliser COUNT(*)

✅ Correct

SELECT categorie_id, COUNT(*)

FROM articles

GROUP BY categorie_id

HAVING COUNT(*) > 3;

HAVING filtre APRÈS le GROUP BY

💡 RĂšgle simple : si vous filtrez sur un rĂ©sultat d'agrĂ©gation (COUNT, SUM...), c'est HAVING !

❌ / ✅ Piùge n°2 : Oublier la condition ON

❌ Catastrophe

SELECT *

FROM articles

JOIN categories

-- Pas de ON !

Produit cartĂ©sien : chaque article × chaque catĂ©gorie

4 articles × 3 catĂ©gories = 12 lignes fausses !

✅ Correct

SELECT *

FROM articles

JOIN categories

ON articles.categorie_id = categories.id

Seules les lignes correspondantes sont combinées

💡 Toujours vĂ©rifier que votre JOIN a une clause ON avec la bonne condition

❌ / ✅ Piùge n°3 : INNER vs LEFT

"Je veux TOUS les articles, mĂȘme ceux sans catĂ©gorie"

❌ INNER JOIN

Exclut les articles sans catégorie

"JS" (categorie_id = NULL) disparaĂźt !

✅ LEFT JOIN

Garde TOUS les articles

"JS" apparaĂźt avec categorie = NULL

💡 Si vous voulez TOUS les Ă©lĂ©ments de la table de gauche → LEFT JOIN

À retenir !

🔗

JOIN = coller deux tables

ON donne la condition de collage

↔

INNER vs LEFT

INNER = correspondances | LEFT = tout Ă  gauche

📊

GROUP BY + agrégation

Regrouper pour compter, sommer, moyenner

🔍

WHERE ≠ HAVING

WHERE = avant GROUP BY | HAVING = aprĂšs

Exercices pratiques

1. Liste des articles avec leur catégorie

Utiliser INNER JOIN entre articles et categories

2. Tous les articles, mĂȘme sans catĂ©gorie

MĂȘme requĂȘte mais avec LEFT JOIN — observer la diffĂ©rence

3. Nombre d'articles par catégorie

GROUP BY + COUNT avec le nom de la catégorie (JOIN + GROUP BY)

4. Catégories avec plus de 2 articles

Ajouter HAVING COUNT(*) > 2 Ă  la requĂȘte prĂ©cĂ©dente

5. Prix moyen des articles par catégorie

Utiliser AVG(prix) avec GROUP BY, trié par prix moyen décroissant

Questions ?

JOIN, GROUP BY et HAVING sont les clĂ©s des requĂȘtes avancĂ©es

Prochaine leçon : Sous-requĂȘtes et requĂȘtes imbriquĂ©es