CodeGym /Cours /SQL SELF /Utilisation des sous-requêtes dans HAVING pour filtrer le...

Utilisation des sous-requêtes dans HAVING pour filtrer les données agrégées

SQL SELF
Niveau 14 , Leçon 2
Disponible

Parfois, on a besoin non seulement de grouper des données et de filtrer le résultat, mais de le faire avec une logique supplémentaire — par exemple, comparer la note moyenne des étudiants dans un groupe avec un critère externe. C’est là que HAVING avec des sous-requêtes entre en jeu — un outil puissant qui te permet de prendre des décisions plus intelligentes directement dans ta requête SQL.

Petit rappel sur HAVING

On va se concentrer sur les sous-requêtes utilisées avec HAVING pour filtrer les données au niveau des valeurs agrégées. Pourquoi ? Parce que WHERE permet de filtrer les lignes individuelles, alors que HAVING s’applique déjà aux données groupées — c’est un autre niveau d’analyse qui élargit tes possibilités.

Avant de plonger dans la combinaison des sous-requêtes et de HAVING, faisons un petit rappel sur ce qu’est HAVING et en quoi il diffère de WHERE.

  • WHERE filtre les lignes avant l’exécution du groupement (GROUP BY).
  • HAVING filtre les données après l’agrégation, quand les données sont déjà groupées.

Imagine que tu analyses des étudiants et leurs notes. Avec WHERE, tu peux exclure les étudiants avec certaines notes minimales, alors que HAVING te permet d’exclure carrément des groupes d’étudiants selon leur moyenne ou leur note maximale.

Exemple de données

Voici une table d’exemple avec des étudiants :

Table students :

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

Exemple d’utilisation de HAVING (sans sous-requêtes)

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > 75;

Résultat :

department avg_grade
Physics 82.5
Math 75.0

La fac "History" n’apparaît pas dans le résultat, parce que sa moyenne est en dessous de 75. Facile, non ? Maintenant, ajoutons un peu de magie avec les sous-requêtes. Dans l’exemple suivant, on peut par exemple filtrer en comparant avec la moyenne générale de toutes les facs.

Sous-requêtes dans HAVING

Les sous-requêtes dans HAVING — c’est super pratique pour ajouter de la flexibilité quand tu filtres des données agrégées. Elles te permettent de comparer des agrégats, comme la moyenne ou le max, avec des valeurs calculées ailleurs dans la base. En gros, tu peux vérifier : "Est-ce que notre résultat est meilleur que la moyenne générale ?"

Exemple : filtrer les facs par note moyenne

Imaginons qu’on veuille trouver les facs où les étudiants bossent mieux que les autres — c’est-à-dire où la moyenne est supérieure à la moyenne de l’université.

Voici nos données :

Table students :

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

D’abord, on récupère la moyenne de tous les étudiants :

SELECT AVG(grade) AS university_avg
FROM students;

Maintenant, on applique une sous-requête dans HAVING :

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

Résultat :

department avg_grade
Physics 82.5

Qu’est-ce qui se passe ici ?

  1. La sous-requête (SELECT AVG(grade) FROM students) calcule la moyenne générale — ici, c’est 77.
  2. La requête principale groupe les étudiants par fac et calcule la moyenne pour chaque fac.
  3. HAVING compare la moyenne de la fac avec la moyenne générale et ne garde que celles qui sont au-dessus.

Comparaison entre WHERE et HAVING

Pour bien piger la différence, imagine que tu veux sélectionner seulement les étudiants qui ont une note au-dessus de la moyenne. Ça, tu peux le faire uniquement avec WHERE :

SELECT name, grade
FROM students
WHERE grade > (SELECT AVG(grade) FROM students);

Résultat (si on prend la table des exemples précédents) :

name grade
Alex 80
Maria 85
Dan 90

Mais si tu veux voir dans quelles facs la moyenne des étudiants est supérieure à la moyenne de l’université, là tu ne peux pas te passer de HAVING — parce que tu filtres des groupes, pas des lignes :

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

Résultat :

department avg_grade
Physics 82.5

En résumé :

  • WHERE agit sur les lignes individuelles avant le groupement.
  • HAVING filtre les groupes après qu’ils aient été agrégés.

Exemple : bosser avec plusieurs agrégats

Regardons un autre cas. Disons qu’on a une table students qui contient les notes des étudiants et leurs facs :

Table students :

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

Maintenant, on veut trouver les facs où :

  1. La moyenne des étudiants est supérieure à la moyenne de l’université.
  2. La note maximale dans la fac est supérieure à 90.

Pour ça, on écrit cette requête :

SELECT department, AVG(grade) AS avg_grade, MAX(grade) AS max_grade
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND MAX(grade) > 90;

Ce qui se passe dans cette requête :

  • AVG(grade) > (SELECT AVG(grade) FROM students) — on vérifie que la fac est en moyenne meilleure que les autres.
  • MAX(grade) > 90 — ça veut dire qu’il y a quelqu’un qui a cartonné à l’exam.

Résultat :

department avg_grade max_grade
Math 92.5 95

La fac "Math" est la seule à avoir à la fois une moyenne supérieure à la générale et un étudiant qui a eu plus de 90.

Exemple : sélectionner les groupes avec l’écart minimal

Imaginons que tu veuilles trouver les groupes où la différence entre la note max et la note min des étudiants est plus petite que la différence dans l’université en général.

Voici la table students avec laquelle on va bosser :

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

On va découper la tâche en étapes :

  1. D’abord, on calcule la différence max-min pour toute l’université :
    SELECT MAX(grade) - MIN(grade) AS range_university
    FROM students;
    
  2. Maintenant, on crée la requête principale et on la combine avec cette sous-requête :
SELECT department, MAX(grade) - MIN(grade) AS range_department
FROM students
GROUP BY department
HAVING (MAX(grade) - MIN(grade)) < ( SELECT MAX(grade) - MIN(grade) FROM students );

Résultat de la requête :

department range_department
Physics 5
Math 5

Les groupes "Physics" et "Math" ont montré des notes plus stables — leur écart est plus petit que celui de l’université en général.

Optimisation des requêtes avec HAVING et sous-requêtes

Faut garder en tête que les sous-requêtes imbriquées peuvent avoir un gros impact sur les perfs, surtout dans les grosses bases. Voilà quelques tips :

Utilise des index. Si la sous-requête s’exécute sur une colonne utilisée dans WHERE ou JOIN, assure-toi qu’il y a un index dessus.

Évite la surcharge de données. Si la sous-requête retourne trop de résultats intermédiaires, découpe-la en étapes ou utilise des tables temporaires.

Profile tes requêtes avec EXPLAIN. Vérifie toujours comment PostgreSQL exécute ta requête. Si tu vois que la sous-requête s’exécute plein de fois, pense à l’optimiser.

Compare avec CTE. Parfois, utiliser WITH (Common Table Expressions) peut être plus rapide et plus lisible. Mais ça, on en parlera dans les prochaines leçons :P

Combiner sous-requêtes, HAVING et GROUP BY

Avec les sous-requêtes dans HAVING, tu peux construire des filtres plus complexes, surtout quand tu dois prendre en compte à la fois des agrégats, des moyennes et d’autres métriques. Tout ça aide à trouver des insights sympas dans les vraies données.

Exemple : comparer les facs par moyenne et nombre d’étudiants

Imaginons que tu veuilles sélectionner les facs où :

  1. La moyenne est supérieure à la moyenne de l’université.
  2. Le nombre d’étudiants est supérieur à celui de la fac avec la plus basse moyenne.

Voici la table de départ students :

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History
Oleg 60 History

La requête :

SELECT department, AVG(grade) AS avg_grade, COUNT(*) AS student_count
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND COUNT(*) > (
       SELECT COUNT(*)
       FROM students
       GROUP BY department
       ORDER BY AVG(grade)
       LIMIT 1
   );

Cette requête montre comment combiner les sous-requêtes dans HAVING et GROUP BY pour analyser selon plusieurs critères à la fois. Résultat :

department avg_grade student_count
Physics 82.5 2
Math 92.5 2

La fac History n’apparaît pas dans le résultat, parce qu’elle a la plus basse moyenne et le plus petit nombre d’étudiants. Physics et Math — les deux sont au-dessus de la moyenne, que ce soit pour les notes ou pour le nombre d’étudiants.

Erreurs courantes et comment les éviter

Erreur avec NULL. Si les données contiennent des NULL, les sous-requêtes avec HAVING peuvent donner des résultats inattendus. Utilise COALESCE pour gérer ces cas :

SELECT AVG(grade)
FROM students 
WHERE grade IS NOT NULL;

Données inutiles dans la sous-requête. Si la sous-requête retourne trop de résultats, ça va impacter les perfs. Précise toujours bien les conditions de ta sous-requête.

Mauvaise compréhension de l’ordre d’exécution. Rappelle-toi que HAVING s’exécute après le groupement, et que les sous-requêtes peuvent s’exécuter avant la requête principale.

Absence d’index. Si les colonnes utilisées dans la sous-requête ne sont pas indexées, ça va sérieusement ralentir l’exécution.

Les sous-requêtes dans HAVING t’ouvrent plein de possibilités pour analyser les données au niveau des agrégats. Tu peux filtrer des groupes selon des conditions complexes, comparer les résultats entre groupes et créer des requêtes analytiques avancées. Bravo, maintenant tu es prêt à utiliser ces connaissances dans des vrais projets !

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION