SQL HAVING Clause
The HAVING clause is used to filter groups created by the GROUP BY clause. It works similarly to the WHERE clause, but with one important difference: WHERE filters individual rows before grouping, while HAVING filters the entire group after aggregation has taken place.
Think of it this way: WHERE decides which students enter the room. GROUP BY arranges them into city groups. HAVING then decides which city groups are shown in the final report.
The Reference Table
| StudentID | StudentName | Age | City | Course | Fees |
|---|---|---|---|---|---|
| 1 | Ravi Sharma | 20 | Delhi | Science | 45000 |
| 2 | Priya Mehta | 22 | Mumbai | Commerce | 52000 |
| 3 | Arjun Nair | 19 | Chennai | Science | 38000 |
| 4 | Sneha Kapoor | 21 | Delhi | Arts | 42000 |
| 5 | Rohit Das | 23 | Kolkata | Commerce | 60000 |
| 6 | Nisha Verma | 20 | Mumbai | Science | 48000 |
| 7 | Karan Joshi | 22 | Delhi | Science | 47000 |
Syntax
SELECT column_name, aggregate_function(column)
FROM table_name
GROUP BY column_name
HAVING condition;Example 1: Cities With More Than 1 Student
SELECT City, COUNT(*) AS TotalStudents
FROM Students
GROUP BY City
HAVING COUNT(*) > 1;Result:
| City | TotalStudents |
|---|---|
| Delhi | 3 |
| Mumbai | 2 |
Chennai and Kolkata each have only 1 student, so they are excluded by the HAVING filter.
Example 2: Courses Where Total Fees Exceed 90000
SELECT Course, SUM(Fees) AS TotalFees
FROM Students
GROUP BY Course
HAVING SUM(Fees) > 90000;Result:
| Course | TotalFees |
|---|---|
| Science | 178000 |
| Commerce | 112000 |
Arts has only 42000 in total fees, which is below the threshold, so it is excluded.
Example 3: Cities Where the Average Fee is Above 45000
SELECT City, AVG(Fees) AS AvgFee
FROM Students
GROUP BY City
HAVING AVG(Fees) > 45000;Result:
| City | AvgFee |
|---|---|
| Mumbai | 50000 |
| Kolkata | 60000 |
WHERE vs HAVING — The Key Difference
| Feature | WHERE | HAVING |
|---|---|---|
| Purpose | Filters individual rows | Filters groups after aggregation |
| When it runs | Before GROUP BY | After GROUP BY |
| Can use aggregate functions? | No | Yes |
| Example | WHERE Age > 20 | HAVING COUNT(*) > 2 |
Using WHERE and HAVING Together
Both clauses can be used in the same query. WHERE filters rows first, then GROUP BY groups the remaining rows, and finally HAVING filters the groups.
SELECT City, COUNT(*) AS TotalStudents
FROM Students
WHERE Course = 'Science'
GROUP BY City
HAVING COUNT(*) >= 1;Step-by-step:
WHERE Course = 'Science'— keeps only Science students (rows 1, 3, 6, 7).GROUP BY City— groups them: Delhi (2), Chennai (1), Mumbai (1).HAVING COUNT(*) >= 1— all groups pass (all have at least 1 student).
HAVING With ORDER BY
SELECT City, COUNT(*) AS TotalStudents
FROM Students
GROUP BY City
HAVING COUNT(*) > 1
ORDER BY TotalStudents DESC;This shows only cities with more than one student, sorted from most to fewest.
Key Points to Remember
HAVINGis used to filter groups, not individual rows.HAVINGmust come afterGROUP BYin the query.- Aggregate functions like
COUNT(),SUM(), andAVG()can be used inHAVINGbut not inWHERE. - Use
WHEREto filter rows before grouping andHAVINGto filter groups after aggregation.
Query Clause Order
| Order | Clause |
|---|---|
| 1 | SELECT |
| 2 | FROM |
| 3 | WHERE |
| 4 | GROUP BY |
| 5 | HAVING |
| 6 | ORDER BY |
| 7 | LIMIT |
Summary
The HAVING clause is the filter for grouped data. It works alongside GROUP BY to show only those groups that satisfy a specific aggregate condition — such as cities with more than 2 students or courses with total fees above a threshold. Understanding the difference between WHERE (row-level filtering) and HAVING (group-level filtering) is a key milestone in mastering SQL.
