SQL Having

The HAVING clause filters the summary rows produced by GROUP BY, allowing conditions that test aggregate values.

Filtering Groups, Not Rows

WHERE cannot be used with aggregate functions like COUNT or SUM, because WHERE is applied before the rows are grouped, when those totals do not yet exist. HAVING solves this. It runs after GROUP BY has formed the groups and calculated their aggregates, so its conditions can refer to those aggregate results.

A good way to remember it: WHERE decides which rows take part in the grouping, and HAVING decides which of the resulting groups are worth keeping.

Departments with more than five employees

SELECT department, COUNT(*) AS headcount
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;

Here every department is grouped and counted first. HAVING then throws away any group whose count is five or fewer, so only the larger departments remain in the output.

HAVING vs WHERE

The two clauses look similar but operate at different stages of the query. Understanding the order they run in explains why each is written the way it is.

ClauseRunsCan use aggregates?Filters
WHEREBefore GROUP BYNoIndividual rows
HAVINGAfter GROUP BYYesGrouped summary rows

You can use both in the same query, and often should. Put simple row conditions in WHERE so fewer rows need to be grouped, then use HAVING to test the totals. In the query below, WHERE keeps only active employees, and HAVING keeps only the departments whose average salary of those active employees exceeds a threshold.

Combining WHERE and HAVING

SELECT department, AVG(salary) AS avg_salary
FROM employees
WHERE status = 'active'
GROUP BY department
HAVING AVG(salary) > 60000
ORDER BY avg_salary DESC;
  • WHERE status = 'active' removes inactive employees before grouping.
  • GROUP BY department forms one group per department.
  • HAVING AVG(salary) > 60000 keeps only high-average departments.
  • ORDER BY sorts the surviving groups by average salary.

Clause Order Matters

SQL expects these clauses in a fixed sequence: SELECT, FROM, WHERE, GROUP BY, HAVING, and finally ORDER BY. Writing HAVING before GROUP BY, or trying to filter aggregates in WHERE, results in an error. Keeping this order in mind makes summary queries much easier to build correctly.

Note: Do not put aggregate conditions such as COUNT(*) > 5 in a WHERE clause. The database will reject it because the aggregate is not yet available at the WHERE stage. Move that condition to HAVING.

Exercise: SQL Having

Why must HAVING be used instead of WHERE to filter on COUNT(*)?