HAVING Clause in MySQL
Introduction
Filtering grouped data requires special handling beyond basic WHERE.
The HAVING clause filters groups after GROUP BY aggregation.
It is widely used for:
- Filtering aggregate results (AVG > threshold)
- Conditional summaries
- Advanced reporting queries
What is HAVING Clause
The HAVING clause filters which groups to include based on aggregate conditions.
It works only with GROUP BY and aggregate functions.
Basic Syntax
sql
SELECT column_name, AGGREGATE_FUNCTION(column)
FROM table_name
GROUP BY column_name
HAVING condition;
Example
sql
SELECT city, AVG(marks) as avg_marks
FROM Students
GROUP BY city
HAVING AVG(marks) > 75;
Shows only cities with average marks above 75.
HAVING with Multiple Conditions
Multiple filters on aggregates.
sql
SELECT city, COUNT(*) as student_count
FROM Students
GROUP BY city
HAVING COUNT(*) > 10 AND AVG(marks) > 70;
HAVING vs WHERE
| Aspect | WHERE | HAVING |
| When Applied | Before GROUP BY | After GROUP BY |
| Filters | Individual rows | Groups/aggregates |
| Aggregate Functions | No | Yes |
| Execution Order | Earlier | Later |
Example: WHERE + HAVING
sql
SELECT city, AVG(marks) as avg_marks
FROM Students
WHERE marks >= 50
GROUP BY city
HAVING AVG(marks) > 75;
WHERE filters rows first, HAVING filters groups.
HAVING with ORDER BY
Sort filtered groups.
sql
SELECT city, AVG(marks) as avg_marks
FROM Students
GROUP BY city
HAVING COUNT(*) > 5
ORDER BY avg_marks DESC;
HAVING with Multiple Aggregates
Filter using different aggregates.
sql
SELECT city, AVG(marks), COUNT(*)
FROM Students
GROUP BY city
HAVING AVG(marks) > 70 AND COUNT(*) > 3;
HAVING with JOIN
Filter grouped JOIN results.
sql
SELECT Courses.course_name, COUNT(Students.id) as enrollment
FROM Students
JOIN Courses ON Students.course_id = Courses.id
GROUP BY Courses.course_name
HAVING COUNT(Students.id) > 5;
HAVING with Subqueries
Complex aggregate conditions.
sql
SELECT city, AVG(marks)
FROM Students
GROUP BY city
HAVING AVG(marks) > (
SELECT AVG(marks) FROM Students
);
HAVING with ROLLUP
Filters ROLLUP subtotals.
sql
SELECT city, AVG(marks)
FROM Students
GROUP BY city WITH ROLLUP
HAVING AVG(marks) > 70;
Execution Order with HAVING
HAVING is applied after:
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
LIMIT
Performance Considerations
- HAVING processes after grouping (use WHERE when possible)
- Index GROUP BY columns
- Avoid complex functions in HAVING
- Limit rows with WHERE before GROUP BY
Important Notes
- HAVING without GROUP BY treats whole result as one group
- Can use column aliases in HAVING
- Multiple HAVING conditions with AND/OR
- Works with COUNT(*), not just grouped columns
- MySQL allows non-standard HAVING usage
Example Scenario
Find cities with >10 students and avg marks >80:
sql
SELECT city, COUNT(*), ROUND(AVG(marks),1) as avg_marks
FROM Students
GROUP BY city
HAVING COUNT(*) > 10 AND AVG(marks) > 80
ORDER BY avg_marks DESC;
Common Mistakes
- Using HAVING instead of WHERE for row filters
- Aggregate functions in WHERE clause
- Forgetting GROUP BY with HAVING
- HAVING before GROUP BY (wrong order)
- Complex conditions slowing queries
Key Points to Remember
- HAVING filters groups after GROUP BY
- WHERE filters rows before GROUP BY
- Only aggregate functions/aliases in HAVING
- Essential for conditional aggregate queries
- Combine WHERE + HAVING for best performance
- Supports AND/OR multiple conditions