Retour à la liste des articles Articles
12 minutes de lecture

Quelle est la différence entre les clauses WHERE et HAVING en SQL ?

La quantité de données dans le monde augmentant de jour en jour, le besoin de gérer et d'interroger les données de manière efficace augmente. SQL offre des moyens de manipuler et d'interroger des données à n'importe quelle échelle. Les clauses WHERE et HAVING, qui en constituent une partie importante, vous permettent de filtrer les données selon vos besoins, mais elles ne sont pas identiques. Nous allons expliquer en détail les différences entre elles.

L'une des tâches les plus courantes dans la manipulation et l'interrogation de données est le filtrage à l'aide des clauses WHERE et HAVING. Bien qu'elles filtrent toutes deux les données sur la base de conditions et/ou de fonctions définies par l'utilisateur, il existe des différences importantes dans le mode d'exécution de chaque clause.

Cet article explique en détail quand utiliser WHERE ou HAVING. Nous expliquerons brièvement le déroulement logique d'une requête SQL comprenant les clauses WHERE et/ou HAVING, puis nous présenterons quelques scénarios et exemples pratiques à résoudre ensemble.

Déroulement logique des requêtes avec WHERE et HAVING

L'ordre dans lequel vous écrivez une requête SQL diffère de l'ordre d'exécution. Prenons l'exemple suivant :

SELECT country_id, city, MIN(salary), MAX(salary) 
FROM hr.emp_details_view
WHERE country_id IN ('US', 'UK')
GROUP BY country_id, city
HAVING MIN(salary) < 15000
ORDER BY country_id; 

Le déroulement logique d'une requête SQL est illustré ci-dessous.

Ordre d'exécution dans les requêtes SQL

Figure 1 : Ordre d'exécution des requêtes SQL.


  1. Le traitement logique d'une requête SQL commence par une instruction FROM, qui rassemble les données des tables énumérées dans la requête. Elle peut également contenir une clause JOIN qui combine deux ou plusieurs tables à l'aide de l'opérateur ON.

    Notez que les conditions utilisées dans la clause WHERE peuvent également être utilisées dans l'opérateur ON de la clause JOIN. De même, les tables combinées à l'aide des conditions de l'opérateur ON de la clause JOIN peuvent être utilisées dans la clause WHERE. Toutefois, vous devez utiliser une clause WHERE uniquement pour les conditions qui filtrent les lignes individuelles. Cela améliore la lisibilité de la requête et permet à l'opérateur ON de gérer uniquement la combinaison des tables, ce qui est son objectif.

  2. L'étape suivante du processus logique est la clause WHERE, qui filtre les enregistrements pour ne retenir que ceux qui répondent aux conditions et/ou aux fonctions définies par l'utilisateur et les transmet.
  3. La clause WHERE est suivie de la clause GROUP BY, qui regroupe les enregistrements reçus à partir de la condition WHERE. Par exemple, la ou les colonnes énumérées dans la clause GROUP BY peuvent être les équipes, et les autres colonnes sont agrégées par la ou les fonctions d'agrégation et affectées au(x) groupe(s)/équipe(s) correspondant(s).
  4. Vient ensuite la clause HAVING, qui filtre les groupes créés dans GROUP BY plutôt que les enregistrements individuels.
  5. À ce stade, le traitement logique revient à la commande SELECT. Elle évalue les colonnes qui vont être envoyées à la sortie. Elle évalue également les mots-clés tels que UNIQUE, DISTINCT et TOP, s'ils sont inclus.
  6. La clause ORDER BY est exécutée à la fin du flux de processus logique. Elle trie les données en fonction de la ou des colonnes qu'elle spécifie et dans l'ordre croissant par défaut.

Tout d'abord, l'essentiel : Spécifier des conditions dans WHERE et HAVING

Il existe un large éventail d'opérateurs qui nous aident à créer et à combiner des conditions dans les clauses WHERE et HAVING:

  • signes de comparaison : <, >, <=, >=, =, <>
  • le test de la valeur nulle : IS NULL, IS NOT NULL
  • caractères génériques : LIKE, '%=_'
  • test d'intervalle : BETWEEN
  • test de présence dans un ensemble : IN
  • test d'existence : EXISTS
  • signes de comparaison quantifiés : ALL, ANY, SOME
  • combinaison logique des conditions : AND, OR, NOT

Les opérandes d'une clause WHERE peuvent inclure les colonnes énumérées dans SELECT, à l'exception des colonnes utilisées dans les fonctions d'agrégation. En revanche, HAVING filtre les valeurs résultantes de la fonction d'agrégation telles que SUM(), COUNT(), et AVG(), entre autres.

Exemples

Les tables employee et emp_details_viewqui sont partiellement illustrés ci-dessous, sont utilisés dans les exemples suivants.

SELECT * 
FROM hr.employees;
EMPLOYEE_IDLAST_NAMEMANAGER_IDHIRE_DATESALARYJOB_ID
101Kochhar10021-SEP-0517000AD_VP
102De Haan10013-JAN-0117000AD_VP
108Greenberg10117-AUG-0212008FI_MGR
103Hunold10203-JAN-069000IT_PROG
105Austin10325-JUN-054800IT_PROG
116Baida11424-DEC-052900PU_CLERK
117Tobias11424-JUL-052800PU_CLERK
145Russell10001-OCT-0414000SA_MAN
148Cambrault10015-OCT-0711000SA_MAN
SELECT * 
FROM hr.emp_details_view;
EMPLOYEE_IDLAST_NAMEFIRST_NAMESALARYDEPARTMENT_IDCOUNTRY_IDCITYLOCATION_ID
103HunoldAlexander900060USSouthlake1400
105AustinDavid480060USSouthlake1400
101KochharNeena1700090USSeattle1700
102De HaanLex1700090USSeattle1700
108GreenbergNancy12008100USSeattle1700
116BaidaShelli290030USSeattle1700
117TobiasSigal280030USSeattle1700
145RussellJohn1400080UKOxford2500
148CambraultGerald1100080UKOxford2500

Scénario I : La clause WHERE

La clause WHERE est utilisée pour filtrer les lignes individuelles de données extraites de la base de données par SELECT. Les enregistrements qui ne répondent pas à la ou aux conditions de WHERE ne sont pas inclus dans le résultat.

Prenons un exemple :

where age > 25 AND city='New York'

Il ne passe que les enregistrements dont l'âge est supérieur à 25 ans et dont city est New York.

Dans la syntaxe de la requête, la clause WHERE est utilisée directement après SELECT et FROM. Elle filtre les lignes individuelles - les enregistrements bruts, non groupés - récupérées par la requête, afin de déterminer quels enregistrements doivent être transmis à la clause GROUP BY.

Examinons le résultat de la requête ci-dessous qui utilise la clause WHERE.

SELECT last_name, hire_date, manager_id 
FROM hr.employees
WHERE last_name LIKE '%a%' 
AND (hire_date BETWEEN '01-JAN-05' AND '01-JAN-07') 
AND manager_id IS NOT NULL;
LAST_NAMEHIRE_DATEMANAGER_ID
Baida24-DEC-05114
Kochhar21-SEP-05100
Tobias24-JUL-05114

L'exemple ci-dessus extrait les colonnes last_name, hire_date et manager_id de la table hr.employee. La clause WHERE filtre la sortie comme suit :

  • last_name doit contenir la lettre "a" ;
  • hire_date doit être supérieur ou égal au 01-JAN-05 et inférieur ou égal au 01-JAN-07 ; et
  • manager_id doit avoir une valeur et ne doit pas être nulle.

Seuls les enregistrements répondant aux conditions de WHERE sont présentés dans la sortie.

Examinons un autre exemple tiré du même scénario :

SELECT employee_id, salary, job_id 
FROM hr.employees
WHERE (salary < 3000 OR salary = 9000) 
AND job_id IN ('IT_PROG', 'FI_MGR', 'PU_CLERK'); 
EMPLOYEE_IDSALARYJOB_ID
1039000IT_PROG
1162900PU_CLERK
1172800PU_CLERK

Cet exemple extrait les colonnes employee_id, salary et job_id de la table hr.employee. La clause WHERE filtre le résultat comme suit :

  • salary doit être inférieur à 3000, ou doit être égal à 9000 ; et
  • job_id doit être égal à toute valeur figurant dans l'opérateur IN.

Comme dans la requête précédente, la sortie ne contient que les enregistrements qui répondent à la condition WHERE.

N'oubliez pas que WHERE ne peut pas contenir de conditions incluant des fonctions d'agrégation. Cette tâche incombe à HAVING.

Scénario II : la clause HAVING

HAVING est utilisée pour filtrer les groupes d'enregistrements créés par la clause GROUP BY. Pour cette raison, la clause HAVING doit suivre la clause GROUP BY. Elle est similaire à la clause WHERE qui filtre la sortie SELECT, à ceci près que WHERE filtre des enregistrements individuels tandis que HAVING filtre des groupes.

Les fonctions agrégées telles que SUM(), MAX(), MIN(), AVG() et COUNT() sont l'objet de la clause HAVING.

Voyons comment cela fonctionne dans les exemples.

SELECT SUM(salary), department_id 
FROM hr.emp_details_view
GROUP BY department_id
HAVING SUM(salary) > 10000; 
SUM(SALARY)DEPARTMENT_ID
3400090
1380060
12008100
2500080

L'exemple ci-dessus énumère les départements ainsi que la somme de tous les salaires de chaque département. Seuls les department_iddont le total des salaires est supérieur à 10000 sont répertoriés dans la sortie. Ceux qui ne remplissent pas la condition fixée par la clause HAVING sont éliminés par filtrage.

Examinons un autre exemple :

SELECT COUNT(employee_id), job_id, salary
FROM hr.employees
WHERE salary > 12000
GROUP BY job_id, salary
HAVING COUNT(employee_id) < 10; 
COUNT(EMPLOYEE_ID)JOB_IDSALARY
1SA_MAN14000
2AD_VP17000
1FI_MGR12008

Cette requête répertorie le nombre d'employés pour chaque combinaison de job_id et de salaire. La clause WHERE filtre les enregistrements en fonction de ceux dont le salaire est supérieur à 12 000. La clause GROUP BY, qui suit WHERE, spécifie le regroupement par les colonnes non agrégées job_id et salary. Enfin, la clause HAVING précise que la valeur agrégée COUNT(employee_id) doit être inférieure à 10.

Scénario III : la clause GROUP BY

WHERE avec une clause GROUP BY

La clause WHERE doit toujours être placée avant GROUP BY. En effet, WHERE filtre des lignes de données individuelles, et non des groupes de lignes. La clause GROUP BY prend les lignes individuelles du résultat du filtre de niveau ligne WHERE pour créer des groupes de lignes.

Voici un exemple :

SELECT job_id, SUM(salary) 
FROM hr.employees
WHERE manager_id IN (100, 101, 102, 103)
GROUP BY job_id; 
JOB_IDSUM(SALARY)
AD_VP34000
FI_MGR12008
IT_PROG13800
SA_MAN25000

La requête ci-dessus est évaluée comme suit :

  1. La clause WHERE filtre d'abord les enregistrements avec manager_id qui ne se trouvent pas dans la liste suivant l'opérateur IN.
  2. La clause GROUP BY regroupe ensuite par job_id les enregistrements qui ont passé la condition WHERE.

La requête calcule le total des salaires des employés gérés par chacun des responsables spécifiés (manager_ids 100, 101, 102, 103). Les salaires des employés relevant d'autres responsables ne sont pas inclus dans le calcul de ce total.

HAVING avec une clause GROUP BY

La clause GROUP BY est souvent utilisée avec les fonctions d'agrégation. Elle crée des valeurs récapitulatives pour les colonnes énumérées dans GROUP BY.

En revanche, HAVING suit toujours une clause GROUP BY, car HAVING travaille avec les groupes créés par GROUP BY.

Considérons un cas utilisant HAVING dans lequel la clause GROUP BY ne peut être omise. C'est le cas s'il y a des colonnes listées dans SELECT qui ne sont pas utilisées par les fonctions d'agrégation, comme department_id dans l'exemple ci-dessous. Ces colonnes non agrégées doivent être listées dans GROUP BY pour le regroupement des données.

SELECT avg(salary), department_id 
FROM hr.emp_details_view
GROUP BY department_id
HAVING avg(salary) < 15000; 
AVG(SALARY)DEPARTMENT_ID
285030
690060
12008100
1250080

Toutefois, HAVING peut être utilisé sans être accompagné de GROUP BY. Notez que dans ce cas, HAVING est appliqué à la totalité de la sortie SELECT, la traitant comme un seul groupe. L'exemple ci-dessous est un exemple :

SELECT round(avg(salary))
FROM hr.emp_details_view
HAVING avg(salary) < 11000; 
ROUND(AVG(SALARY))
10056

La requête renvoie une seule valeur contenant la moyenne de tous les salaires. Notez que la clause HAVING impose une limite à cette valeur agrégée. Si la moyenne calculée avait été supérieure à 11000, la requête n'aurait renvoyé aucun enregistrement.

L'exemple suivant est un exemple de pseudo-agrégation, dans lequel des fonctions d'agrégation sont utilisées à la place de la clause GROUP BY:

SELECT MIN(first_name), MIN(department_id), MAX(salary)
FROM hr.emp_details_view
HAVING MIN(salary) > 1000; 
MIN(FIRST_NAME)MIN(DEPARTMENT_ID)MAX(SALARY)
Alexander3017000

La fonction MIN() est utilisée ici sur les colonnes first_name et department_id. Elle n'est pas appelée dans le cadre d'une utilisation typique des fonctions d'agrégation, mais plutôt dans le but d'éviter un GROUP BY. Dans ce cas, la sortie est un seul enregistrement de données qui se compose de la valeur minimale pour first_name, de la valeur minimale pour department_id et de la valeur maximale pour le salaire, chacune de la table entière. Notez que ces 3 valeurs peuvent provenir de 3 enregistrements différents, comme c'est le cas dans cet exemple. En outre, dans ce cas, si le salaire minimum pour l'ensemble de la table était égal ou inférieur à 1000, la requête ne renverrait aucun enregistrement.

Cet exemple particulier est un peu artificiel pour que nous puissions vous montrer quelque chose de plus simple. Bien que moins courante que GROUP BY, la pseudo-agrégation peut être utilisée dans des requêtes qui traitent toutes les lignes incluses comme un seul groupe.

L'utilisation de HAVING avec ou sans GROUP BY peut varier selon la base de données. Si vous souhaitez en savoir plus sur la clause GROUP BY, consultez l'article Clause GROUP BY.

Scénario IV : utilisation conjointe des clauses WHERE et HAVING

La syntaxe permettant d'utiliser WHERE et HAVING dans une même requête suit l'ordre spécifique présenté ci-dessous :

  1. SELECTLa clause WHERE : sélectionne les colonnes de la base de données à transmettre au résultat.
  2. FROMLa clause WHERE : liste les tables à utiliser dans la requête.
  3. WHEREFiltre les enregistrements individuels.
  4. GROUP BY: regroupe les enregistrements en fonction de la ou des colonnes spécifiées.
  5. HAVING: filtre les groupes définis par GROUP BY.
  6. ORDER BY: trie les enregistrements en sortie selon la ou les colonnes spécifiées.

Examinons un exemple qui utilise tous les éléments ci-dessus.

SELECT country_id, city, MIN(salary), MAX(salary) 
FROM hr.emp_details_view
WHERE country_id IN ('US', 'UK')
GROUP BY country_id, city
HAVING MIN(salary) < 15000
ORDER BY country_id; 
COUNTRY_IDCITYMIN(SALARY)MAX(SALARY)
UKOxford1100014000
USSeattle280017000
USSouthlake48009000

La requête récupère le minimum salary et le maximum salary pour chaque combinaison de city et country_id, ce dernier étant limité aux États-Unis et au Royaume-Uni par la condition WHERE.

Supposons que vous ayez 10 enregistrements avec les États-Unis comme country_id, et que parmi ceux-ci, il y ait 5 enregistrements avec New York comme ville et 5 autres avec Los Angeles comme ville. Lorsque vous exécutez la requête ci-dessus, ces 10 enregistrements deviennent 2 enregistrements :

  • un enregistrement pour les États-Unis et New York, avec ses salaires minimum et maximum,
  • un autre enregistrement pour les États-Unis et Los Angeles, également avec ses salaires minimum et maximum.

Pour plus d'informations sur les différences entre WHERE et HAVING, veuillez consulter l'article sur la comparaison de SQL HAVING et WHERE.

Scénario V : WHERE et HAVING avec JOINs

L'exemple suivant va un peu plus loin en utilisant un JOIN avec WHERE et HAVING. Il joint les données de deux tables sur la base d'une colonne commune, employee_id. Les clauses WHERE et HAVING suivent l'ordre d'exécution que nous venons d'évoquer.

SELECT e.job_id, edv.location_id, SUM(e.salary)
FROM hr.employees e
JOIN hr.emp_details_view edv
ON e.employee_id=edv.employee_id
WHERE e.job_id IN ('IT_PROG', 'SA_MAN')
GROUP BY e.job_id, edv.location_id
HAVING SUM(e.salary) > 5000
ORDER BY e.job_id; 
JOB_IDLOCATION_IDSUM(E.SALARY)
IT_PROG140013800
SA_MAN250025000

Cet exemple sélectionne la colonne job_id dans le tableau . hr.employeela colonne location_id de la table hr.emp_details_viewet le total des salaires pour chaque combinaison de job_id et location_id. La condition permettant de joindre les tables est indiquée dans l'opérateur ON. Cette condition est basée sur la colonne commune, employee_id.

Ensuite, la clause WHERE filtre les enregistrements en ne retenant que ceux de job_ids qui sont soit IT_PROG soit SA_MAN. La clause GROUP BY regroupe les enregistrements par la combinaison des colonnes job_id et location_id. Enfin, la clause HAVING filtre la valeur agrégée SUM(e.salary) sur les enregistrements supérieurs à 5000.

La clause ORDER BY trie les enregistrements de sortie (dans l'ordre croissant par défaut) par la colonne job_id. Pour en savoir plus sur les clauses GROUP BY et ORDER BY, consultez l'article GROUP BY vs. ORDER BY.

Différence entre les clauses WHERE et HAVING en SQL

Voici un résumé en un coup d'œil des différences entre les clauses WHERE et HAVING en SQL :

WHERE clauseHAVING clause
FilteringFilters individual rows fetched by SELECT, i.e., the output of the SELECT commandFilters groups of rows created by GROUP BY, i.e., the output of the GROUP BY clause
ConditionsCannot have conditions containing aggregate functionsFocuses on conditions containing aggregate functions
SyntaxWHERE comes before GROUP BY in the syntaxHAVING comes after GROUP BY in the syntax
Order of evaluationWHERE clause is evaluated before GROUP BY in the process flow, before aggregation is performedHAVING clause is evaluated after GROUP BY in the process flow, after aggregation is performed
With/without GROUP BYCan be used with or without GROUP BY, since WHERE and GROUP BY are not relatedShould be used with GROUP BY; otherwise, the output of SELECT is treated as one group

WHERE et HAVING constituent une partie importante de l'apprentissage de SQL. Consultez le SQL pour les débutants cours pour vous aider à développer vos compétences en SQL.