HAVING vs WHERE in SQL

⏱️ 35 sec read 💾 SQL

WHERE filters individual rows before grouping happens; HAVING filters groups after GROUP BY and aggregation. If your condition uses an aggregate like SUM() or COUNT(), it belongs in HAVING.

When Does WHERE Run vs HAVING?

SQL evaluates clauses in this logical order: FROMWHEREGROUP BYHAVINGSELECTORDER BY. That means WHERE sees raw rows and cannot reference aggregates, while HAVING sees one row per group and can.

-- WHERE can't do this; the aggregate doesn't exist yet:
SELECT region, SUM(amount)
FROM sales
WHERE SUM(amount) > 1000   -- ERROR: aggregate not allowed here
GROUP BY region;

How Do You Filter on an Aggregate?

Put the aggregate condition in HAVING, which runs after the groups are built.

SELECT region, SUM(amount) AS total_sales
FROM sales
GROUP BY region
HAVING SUM(amount) > 1000;
-- region | total_sales
-- West   | 4200
-- East   | 1850

Can You Use WHERE and HAVING Together?

Yes, and you usually should: WHERE cuts rows early (cheap), then HAVING filters the resulting groups.

-- Regions with over $1,000 in 2025 sales, excluding refunds
SELECT region, SUM(amount) AS total_sales
FROM sales
WHERE sale_date >= '2025-01-01'
  AND amount > 0                 -- row-level: runs first
GROUP BY region
HAVING SUM(amount) > 1000;       -- group-level: runs after

Moving row-level conditions into WHERE shrinks the data before grouping, which is faster and lets indexes help.

Common Pitfalls

For more on grouping itself, see GROUP BY and HAVING and the rundown of SQL aggregate functions.

Pro Tip: If a condition doesn't contain an aggregate function, put it in WHERE — even if it "works" in HAVING. The optimizer can push WHERE predicates into index scans; HAVING always runs after the expensive grouping step.

← Back to SQL Tips