MySQL Having

HAVING filters the groups produced by GROUP BY based on aggregate conditions, something WHERE cannot do because WHERE only sees individual rows before they are grouped.

What HAVING Does

When a query uses GROUP BY, MySQL collapses many rows into one row per group and can compute aggregate values like COUNT, SUM, or AVG for each group. HAVING runs after that collapsing step, so it can filter based on those aggregate results — for example, keeping only departments with more than five employees, a condition that doesn't exist until the grouping happens.

HAVING vs WHERE

MySQL conceptually processes a query in this order: FROM, then WHERE, then GROUP BY, then HAVING, then SELECT, then ORDER BY. WHERE filters individual rows before any grouping occurs, so it can never reference an aggregate function like COUNT(*) or AVG(salary) — those values simply don't exist yet at that stage. HAVING runs after GROUP BY, so it operates on the summarized groups instead of raw rows.

  • WHERE filters individual rows before grouping happens
  • HAVING filters groups after aggregation has been computed
  • WHERE cannot reference aggregate functions such as COUNT, SUM, or AVG
  • A single query can use both: WHERE to trim rows early, HAVING to filter the resulting groups

Departments With More Than Five Employees

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

Combining WHERE and HAVING in the same query is common and efficient: WHERE removes rows you never want to consider at all, shrinking the data before grouping, and HAVING then filters based on the aggregates computed from what's left.

Recent Hires, Grouped and Filtered by Average Salary

SELECT department, AVG(salary) AS avg_salary
FROM employees
WHERE hire_date > '2020-01-01'
GROUP BY department
HAVING AVG(salary) > 60000;
departmentemployee_countpasses HAVING COUNT(*) > 5
Sales8Yes
Support3No
Engineering6Yes

High-Spending Customers Using an Alias

SELECT customer_id, SUM(amount) AS total_spent
FROM orders
GROUP BY customer_id
HAVING total_spent > 1000;
Note: MySQL is more permissive than the SQL standard here: it lets HAVING reference a SELECT alias like total_spent directly, even though strictly HAVING is supposed to only see expressions, not the final column names.
Note: Don't reach for HAVING to filter rows that WHERE could handle instead — filtering early with WHERE reduces the number of rows MySQL has to group and aggregate, which is almost always faster than grouping everything and discarding groups afterward.

Exercise: MySQL Having

What is the key difference between WHERE and HAVING?