Filtering Groups with HAVING
Learn when to use WHERE and when to use HAVING to filter SQLite grouped summaries.
GROUP BY lets you create one summary row per group. The next question is: how do you keep only the groups whose summary meets a condition?
For example:
- Departments with more than 2 employees
- Products with total sales over 100
- Customers whose average order is at least 25
These are not row-by-row conditions. They are conditions about groups after aggregation. That is what HAVING is for.
WHERE filters rows before groups exist
WHERE runs before grouping. At that moment, there are only individual rows. There are no group totals yet, so WHERE COUNT(*) > 2 does not make sense.
Think of it like making piles of receipts:
WHEREdecides which receipts are allowed into the piles.GROUP BYmakes the piles.- Aggregates like
COUNTandSUMsummarize each pile. HAVINGdecides which completed piles stay in the result.
HAVING filters groups
HAVING runs after GROUP BY, so it can use aggregate functions such as COUNT(*), SUM(amount), and AVG(salary).
Only Engineering survives because it has 3 employees. Sales has 2 and Marketing has 1, so those groups are filtered out.
What does HAVING COUNT(*) > 2 filter?
Individual employee rows before grouping.
Completed groups whose row count is greater than 2.
Table columns before the query starts.
The order of rows in the final result.
WHERE and HAVING together
WHERE and HAVING are not competitors. They do different jobs at different moments.
| Clause | Filters | When it happens | Example |
|---|---|---|---|
WHERE | individual rows | before grouping | only full-time employees |
HAVING | groups | after aggregation | departments with average salary over 80000 |
Use both when you need both jobs.
Read it in stages:
WHERE is_intern = 0keeps only non-intern rows.GROUP BY deptmakes department groups.AVG(salary)calculates each group's average.HAVING AVG(salary) > 80000keeps only groups with a high average.
Filtering by totals
HAVING is also common with SUM. This query keeps only products whose total sales are at least 30.
The SUM(amount) value is created per product group, so the condition belongs in HAVING.
Filtering rows first can change group results
Because WHERE happens before grouping, it can change the totals that HAVING sees.
This query first keeps only online sales rows. Then it groups and totals those online rows. Finally, HAVING keeps products with at least 20 in online sales.
Rule of thumb
If the condition mentions an aggregate function like COUNT, SUM, AVG, MIN, or MAX, it usually belongs in HAVING. If it checks ordinary row values, it usually belongs in WHERE.
The clause order so far
The written order of a grouped query is fixed:
SELECT columns_and_aggregates
FROM table_name
WHERE row_condition
GROUP BY group_columns
HAVING group_condition
ORDER BY sort_columns;The logical flow is:
The most important part for this page is:
WHEREhappens beforeGROUP BY;HAVINGhappens afterGROUP BY.
HAVING without showing the aggregate condition
The aggregate in HAVING does not have to appear in the final output, though showing it is often clearer for beginners.
This returns customers who have at least two orders, even though the count is not displayed.
Check your understanding
Why can't WHERE COUNT(*) > 2 filter groups with more than two rows?
Because COUNT(*) is not allowed in SQLite.
Because WHERE runs before groups and counts exist.
Because WHERE can only be used with text columns.
Because numbers cannot be compared with >.
You want only orders from 2025, then only customers whose total spending is over 100. Where should the conditions go?
Put both conditions in WHERE.
Put both conditions in HAVING.
Put the 2025 row filter in WHERE and the total-spending filter in HAVING.
Put neither condition in SQL.
Which condition most clearly belongs in HAVING?
customer = 'Ada'
order_date = '2025-01-01'
SUM(amount) > 500
channel = 'Online'
In the logical query flow, which order is right for these three steps?
HAVING → WHERE → GROUP BY
GROUP BY → WHERE → HAVING
WHERE → GROUP BY → HAVING
WHERE → HAVING → GROUP BY