SQL HAVING vs WHERE: What is the Difference?
Both WHERE and HAVING filter, but at different moments. WHERE decides which rows go into the groups; HAVING decides which finished groups come out. Mixing them up is one of the most common SQL interview errors.
Examples run in MySQL 8 and PostgreSQL · Updated
In short
WHEREfilters rows, beforeGROUP BY.HAVINGfilters groups, after aggregates are computed.- Aggregates like
COUNT(*)are not allowed inWHERE. - Use both in one query when you need row and group conditions.
The order of operations
The database runs FROM → WHERE → GROUP BY → aggregates → HAVING → SELECT → ORDER BY. When WHERE runs, groups do not exist yet, which is why WHERE COUNT(*) > 2 is an error.
A condition on a plain column (salary > 60000) belongs in WHERE: it is clearer and lets the database discard rows early. A condition on an aggregate (COUNT(*) >= 2) belongs in HAVING.
The example tables
Every example on this page uses these tables. They are small on purpose, so you can check each result by eye.
employees
| emp_id | name | dept_id | salary | manager_id | hire_date |
|---|---|---|---|---|---|
| 1 | Aarav | 10 | 95000 | NULL | 2021-03-01 |
| 2 | Diya | 10 | 88000 | 1 | 2022-06-15 |
| 3 | Kabir | 10 | 72000 | 1 | 2023-01-10 |
| 4 | Meera | 20 | 64000 | NULL | 2020-11-20 |
| 5 | Rohan | 20 | 58000 | 4 | 2024-02-05 |
| 6 | Isha | 30 | 61000 | NULL | 2022-09-12 |
| 7 | Arjun | 20 | 64000 | 4 | 2023-07-30 |
| 8 | Zoya | NULL | 52000 | NULL | 2024-05-01 |
Examples
Filter groups with HAVING
Departments with at least two employees. Marketing and the NULL group have one each, so they are removed after counting.
SELECT dept_id, COUNT(*) AS employees
FROM employees
GROUP BY dept_id
HAVING COUNT(*) >= 2
ORDER BY dept_id;| dept_id | employees |
|---|---|
| 10 | 3 |
| 20 | 3 |
WHERE and HAVING together
WHERE first keeps only salaries above 60,000 (dropping Rohan and Zoya); HAVING then keeps departments with at least two such people.
SELECT dept_id, COUNT(*) AS well_paid
FROM employees
WHERE salary > 60000
GROUP BY dept_id
HAVING COUNT(*) >= 2
ORDER BY dept_id;| dept_id | well_paid |
|---|---|
| 10 | 3 |
| 20 | 2 |
Common mistakes
- Aggregate in WHERE.
WHERE COUNT(*) > 2fails. Move the condition toHAVING. - Row filter in HAVING. Putting
salary > 60000inHAVINGchanges the meaning or fails, because after grouping there is no single salary. Filter rows inWHERE. - Using a SELECT alias in HAVING. PostgreSQL does not allow
HAVING employees >= 2with a SELECT alias; repeat the aggregate (HAVING COUNT(*) >= 2), which works everywhere.
Practise HAVING vs WHERE
Interview questions that use this concept, easiest first. Each one has the step-by-step visualizer and answer checking.
High-Volume Cities by Completed Trips
Summarize ride revenue for cities that have enough completed trips.
Popular Email Domains
Count signups per email domain, ignoring letter case, and keep the popular ones.
Authors With Five Published Books
Find authors who have at least five published books in the catalog.
Brands With a Premium Range
Find brands that sell at least two different products at an average price above $3.
Customers Buying Both Brands
Find customers who bought at least one product from each of two brands.
Composer Post Rate by App
Yesterday's share of opened composers that ended in a published post, per app.
Want a full set? Work through the GROUP BY & Aggregation study plan.
Questions people ask
- Can HAVING be used without GROUP BY?
- Yes; the whole table is then one group. It is rare in practice, for example HAVING COUNT(*) > 0.
- Which is faster, WHERE or HAVING?
- For conditions on plain columns, WHERE, because rows are removed before the grouping work is done.