JOIN, agrégation, GROUP BY et HAVING
Utilisez les flĂšches, cliquez ou glissez pour naviguer
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
Pourquoi JOIN ?
Croiser des données entre plusieurs tables
INNER JOIN
Le plus courant â seulement les correspondances
LEFT JOIN
Garder toutes les lignes de gauche mĂȘme sans correspondance
Fonctions d'agrégation & GROUP BY
COUNT, SUM, AVG, MIN, MAX â regrouper et calculer
HAVING & l'ordre d'exécution SQL
Filtrer les groupes et comprendre l'ordre
Module 1
Croiser des données entre plusieurs tables
Les données sont éclatées dans plusieurs tables
Comment les rassembler ?
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 !
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
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
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
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
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
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
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
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
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
COUNT, SUM, AVG, MIN, MAX
Transformer plusieurs lignes en une seule valeur
Compter, additionner, moyenner...
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 â 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 â 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 !
Nombre de lignes
Somme des valeurs
Moyenne des valeurs
Valeur minimale
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
Regrouper pour calculer par catégorie
Au lieu de compter tous les articles...
Compter les articles par catégorie
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...)
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
Filtrer les groupes â pourquoi WHERE ne suffit pas
WHERE filtre les lignes
HAVING filtre les groupes
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é !
â 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
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
Pas l'ordre d'Ă©criture, mais l'ordre dans lequel SQL traite la requĂȘte !
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(*)
â 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 !
â 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
"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
đ
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
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
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