Skip to main content
eLearner.app
Module 3 · Lesson 4 of 412/57 in the course~8 min
Module lessons (4/4)

Filtering groups: HAVING

Once you have a result per group (previous lesson), you often want to keep only the groups that satisfy a condition: departments with more than 2 people, projects with an average budget above 100k, users with at least 10 orders. To do this WHERE isn't enough.

Why WHERE isn't enough

WHERE filters the table rows, before aggregation. So it can't see the result of COUNT(*) or AVG(...): by the time WHERE runs, those values haven't been computed yet.

SQL
-- ERROR: COUNT(*) doesn't exist yet when WHERE is evaluated
SELECT department_id, COUNT(*) AS count
FROM   employees
WHERE  COUNT(*) > 2     -- ❌ doesn't work
GROUP BY department_id;

To filter after aggregation there's a dedicated clause: HAVING.

The syntax

SQL
SELECT grouping_column, AGGREGATE_FUNCTION(...)
FROM   table
GROUP BY grouping_column
HAVING   condition_on_aggregate;
SQL
-- Departments with more than 2 employees:
SELECT department_id, COUNT(*) AS count
FROM   employees
GROUP BY department_id
HAVING   COUNT(*) > 2;

HAVING is to GROUP BY what WHERE is to FROM: the same idea ("filter"), but applied after grouping, on the aggregate.

The logical order of clauses

To never confuse WHERE and HAVING again, remember the order in which SQL mentally executes them:

  1. FROM / JOIN — builds the starting table by joining sources.
  2. WHERE — drops the rows you don't care about.
  3. GROUP BY — collects the remaining rows into groups.
  4. HAVING — drops the groups you don't care about.
  5. SELECT — computes the final columns (including aggregates).
  6. ORDER BY / LIMIT — reorder and cut.

Important notes:

  • in WHERE you can only use table columns;
  • in HAVING you can use grouping columns and aggregates.
SQL
-- Classic combo: filter first, group, filter the groups, sort.
SELECT d.name, AVG(e.salary) AS average
FROM   employees   AS e
JOIN   departments AS d ON e.department_id = d.id
WHERE  e.hired_on >= '2018-01-01'   -- filter rows
GROUP BY d.name
HAVING   AVG(e.salary) > 35000      -- filter groups
ORDER BY average DESC;

Try it

Exercise#sql.m3.l4.e1
Attempts: 0Loading…

For each department, show the name (departments.name) and the employee count, BUT only for departments with at least 2 employees. Two columns, one row per qualifying department.

Loading editor…
Show hint

The 'at least 2' condition becomes COUNT(*) >= 2 in HAVING.

Solution available after 3 attempts

Review exercise

Exercise#sql.m3.l4.e2
Attempts: 0Loading…

Show the departments (departments.name) whose average salary exceeds 35000. Consider only employees hired from 2018 onwards. Two columns: name and the average. Sort by average descending.

Loading editor…
Show hint

WHERE filters the rows (employees hired from 2018), HAVING filters the groups (average > 35000).

Solution available after 3 attempts