SQL

What is the difference between GROUP BY and HAVING clauses?

WHERE filters rows. HAVING filters groups. That's the core difference.

29 Apr 2024

What is the difference between GROUP BY and HAVING clauses?

WHERE filters rows. HAVING filters groups. That's the core difference.

I've seen developers use WHERE when they need HAVING (and vice versa) because the distinction isn't obvious at first. Here's how to think about it.

GROUP BY: create the groups

GROUP BY takes all rows with the same value in a column and collapses them into one group. You then apply aggregate functions (SUM, COUNT, AVG) to each group.

Sql
SELECT product_category, SUM(total_sales) AS total_sales
FROM orders
GROUP BY product_category

This returns one row per category with the total sales for each. Every row in orders gets assigned to a group based on its product_category value.

HAVING: filter after grouping

HAVING filters groups after GROUP BY has run. It works on aggregate values.

Sql
SELECT product_category, SUM(total_sales) AS total_sales
FROM orders
GROUP BY product_category
HAVING SUM(total_sales) > 1000

This only returns categories where total sales exceed 1000. You can't do this with WHERE because WHERE runs before grouping -- it doesn't know what the sum will be yet.

The execution order

This is the key insight:

  1. FROM -- Pick the table
  2. WHERE -- Filter individual rows
  3. GROUP BY -- Create groups from remaining rows
  4. Aggregate functions run (SUM, COUNT, etc.)
  5. HAVING -- Filter groups based on aggregate results
  6. SELECT -- Choose which columns to return
  7. ORDER BY -- Sort the output

WHERE and HAVING both filter. But they operate at different stages. WHERE sees individual rows. HAVING sees grouped results.

When to use which

  • Use WHERE when filtering on raw column values: WHERE status = 'active'
  • Use HAVING when filtering on aggregate results: HAVING COUNT(*) > 5
  • Use both when you need to filter rows first, then filter groups: "Show categories with total sales over $1000, but only count orders from 2024."
Sql
SELECT product_category, SUM(total_sales) AS total_sales
FROM orders
WHERE order_year = 2024
GROUP BY product_category
HAVING SUM(total_sales) > 1000

WHERE narrows the rows to 2024. GROUP BY groups by category. HAVING keeps only the high-revenue categories. Each clause does its job at the right stage.

Keep reading