Learner can choose WHERE to filter rows before aggregation and HAVING to filter groups after aggregation, and can fix a query that uses the wrong one.
WHERE filters rows; HAVING filters groups
Two filters at different moments
SQL executes clauses in a specific logical order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. This order tells you exactly when each filter runs.
WHERE runs before GROUP BY. It filters individual rows from the raw table. You can only refer to plain column values here — aggregate functions like SUM() or COUNT() are not yet computed at this stage. Example: WHERE city = 'London' keeps only London rows before any grouping happens.
HAVING runs after GROUP BY. It filters groups based on their aggregate result. This is where you write conditions like HAVING COUNT(*) > 2 or HAVING SUM(amount) >= 100. You cannot put these in WHERE because the aggregate does not exist yet.
The classic beginner error: WHERE SUM(amount) > 100 — this fails because SUM has not been computed when WHERE runs. Move it to HAVING.
You can use both in the same query. For example:
SELECT city, SUM(amount)
FROM orders
WHERE status = 'paid'
GROUP BY city
HAVING SUM(amount) > 50;
This first keeps only paid orders (WHERE), then groups by city, then keeps only cities whose paid total exceeds 50 (HAVING).
Lesson notes
Two filters at different moments
SQL executes clauses in a specific logical order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. This order tells you exactly when each filter runs.
WHERE runs before GROUP BY. It filters individual rows from the raw table. You can only refer to plain column values here — aggregate functions like SUM() or COUNT() are not yet computed at this stage. Example: WHERE city = 'London' keeps only London rows before any grouping happens.
HAVING runs after GROUP BY. It filters groups based on their aggregate result. This is where you write conditions like HAVING COUNT(*) > 2 or HAVING SUM(amount) >= 100. You cannot put these in WHERE because the aggregate does not exist yet.
The classic beginner error: WHERE SUM(amount) > 100 — this fails because SUM has not been computed when WHERE runs. Move it to HAVING.
You can use both in the same query. For example:
SELECT city, SUM(amount)
FROM orders
WHERE status = 'paid'
GROUP BY city
HAVING SUM(amount) > 50;
This first keeps only paid orders (WHERE), then groups by city, then keeps only cities whose paid total exceeds 50 (HAVING).