HAVING Clause
Learn how to filter grouped and aggregated results using HAVING, and how it differs from WHERE.
Introduction
In the previous lesson, GROUP BY let you summarize students per grade — counting them, averaging their marks, and so on. But what if you only want to see the grades that have more than 2 students? WHERE cannot help here, because WHERE filters individual rows before any grouping happens.
HAVING solves exactly this problem. It filters groups after GROUP BY has already summarized the data, letting you keep only the groups that meet a certain condition.
- What HAVING does and when to use it.
- The key difference between WHERE and HAVING.
- A worked example finding grades with more than 2 students.
- The correct order of WHERE, GROUP BY, HAVING, and ORDER BY in a query.
What is HAVING?
HAVING is a clause used to filter the results of a GROUP BY, based on the value of an aggregate function like COUNT(), AVG(), or SUM(). It is written directly after GROUP BY.
Using the same students table from the previous lesson:
| id | name | grade | marks |
|---|---|---|---|
| 1 | Alex | A | 92 |
| 2 | Sam | B | 74 |
| 3 | Priya | A | 88 |
| 4 | Jon | C | 55 |
| 5 | Maya | B | 69 |
| 6 | Leo | A | 95 |
Grouping by grade and counting gives three groups: A has 3 students, B has 2, and C has 1. HAVING lets you keep only the groups whose count satisfies a condition.
SELECT grade, COUNT(*) AS total_studentsFROM studentsGROUP BY gradeHAVING COUNT(*) > 2;Click Run to see what this code prints.
Only grade "A" is returned, because it is the only group where COUNT(*) is greater than 2. Grades B (2 students) and C (1 student) are filtered out.
WHERE vs HAVING
The single most important distinction is timing: WHERE filters individual rows before grouping happens, while HAVING filters entire groups after grouping and aggregation have already happened.
WHERE
- Filters individual rows.
- Runs before GROUP BY.
- Cannot reference aggregate functions like COUNT(*) or AVG().
- Example: WHERE marks > 60
HAVING
- Filters entire groups.
- Runs after GROUP BY.
- Can reference aggregate functions like COUNT(*) or AVG().
- Example: HAVING COUNT(*) > 2
-- WHERE: keep only rows where marks is above 60, BEFORE groupingSELECT grade, COUNT(*) AS total_studentsFROM studentsWHERE marks > 60GROUP BY grade;Click Run to see what this code prints.
Writing WHERE COUNT(*) > 2 is invalid in MySQL, because WHERE runs before aggregation exists — COUNT(*) has not been calculated yet at that point. The condition must be written as HAVING COUNT(*) > 2 instead.
Worked Example: Grades With More Than 2 Students
You can combine WHERE and HAVING in the same query — WHERE filters raw rows first, then GROUP BY summarizes what remains, and HAVING filters those summarized groups.
SELECT grade, COUNT(*) AS total_students, AVG(marks) AS average_marksFROM studentsWHERE marks >= 50GROUP BY gradeHAVING COUNT(*) > 1ORDER BY total_students DESC;Click Run to see what this code prints.
Here, WHERE marks >= 50 keeps every row (all marks already satisfy this in the sample data), GROUP BY grade creates the three grade groups, HAVING COUNT(*) > 1 removes grade "C" (only 1 student), and ORDER BY sorts the remaining groups by size.
Clause Order
MySQL requires clauses to be written in a specific order in your query. Understanding this order helps you reason about exactly what each clause is filtering.
| Order | Clause | Purpose |
|---|---|---|
| 1 | WHERE | Filters individual rows before any grouping. |
| 2 | GROUP BY | Groups the remaining rows by shared column values. |
| 3 | HAVING | Filters the resulting groups, often using aggregate functions. |
| 4 | ORDER BY | Sorts the final, filtered set of grouped rows. |
SELECT grade, COUNT(*) AS total_studentsFROM studentsWHERE marks >= 50 -- 1. filter rowsGROUP BY grade -- 2. group remaining rowsHAVING COUNT(*) > 1 -- 3. filter groupsORDER BY total_students DESC; -- 4. sort final resultThink of it as: WHERE picks rows, GROUP BY buckets them, HAVING picks buckets, ORDER BY arranges what is left. Writing the clauses out of this order causes a syntax error.
Common Mistakes
- Using WHERE with an aggregate function like WHERE COUNT(*) > 2 — this is invalid; use HAVING instead.
- Placing HAVING before GROUP BY in the query — HAVING must always come after GROUP BY.
- Using HAVING to filter individual rows when WHERE would be faster and more appropriate.
- Forgetting ORDER BY must come after HAVING, not before.
Best Practices
- Use WHERE whenever possible, since filtering rows early is more efficient than filtering after grouping.
- Reserve HAVING specifically for conditions that depend on an aggregate function.
- Always write clauses in the order WHERE, GROUP BY, HAVING, ORDER BY.
- Alias aggregate expressions with AS so HAVING conditions stay readable.
Frequently Asked Questions
It is legal in MySQL, but unusual — without GROUP BY, the whole table is treated as one group, so HAVING behaves like a WHERE clause applied to a single aggregate result.
Yes, MySQL allows this (unlike some other databases), so HAVING total_students > 2 works if total_students was aliased in SELECT.
It can be, because WHERE reduces the number of rows before the expensive grouping and aggregation work happens, while HAVING filters after that work is already done.
Yes, using AND, OR, and NOT, exactly like in a WHERE clause — for example HAVING COUNT(*) > 1 AND AVG(marks) > 60.
Key Takeaways
- HAVING filters groups produced by GROUP BY, after aggregation has happened.
- WHERE filters individual rows before any grouping takes place.
- HAVING can reference aggregate functions like COUNT(*) and AVG(); WHERE cannot.
- The correct clause order is WHERE, then GROUP BY, then HAVING, then ORDER BY.
Summary
HAVING completes the picture started by GROUP BY: it lets you filter down to only the groups that matter, based on aggregate conditions like a minimum count or average.
In this lesson, you learned what HAVING does, how it differs from WHERE, and the strict clause order MySQL enforces. Next, you will step back and get a conceptual overview of SQL operators as a whole.
- You understand what HAVING filters and when it runs.
- You can clearly explain the difference between WHERE and HAVING.
- You know the correct order of WHERE, GROUP BY, HAVING, and ORDER BY.
- You are ready to explore SQL operators as a category.