What is the difference between WHERE and HAVING in SQL?

WHERE filters rows before grouping; HAVING filters groups after GROUP BY. You cannot use aggregate functions in WHERE.

SQLPostgreSQL

Overview

Both `WHERE` and `HAVING` filter query results, but at different stages. `WHERE` runs before aggregation and filters individual rows. `HAVING` runs after `GROUP BY` and filters the aggregated groups.

Key Points

  • β€’WHERE: filters rows before GROUP BY is applied. Cannot reference aggregate functions.
  • β€’HAVING: filters groups after GROUP BY. Can reference aggregates like COUNT, SUM, AVG.
  • β€’You can use both in the same query: WHERE narrows rows first, then HAVING narrows groups.
  • β€’HAVING without GROUP BY applies to the whole result set as a single group.
  • β€’Query execution order: FROM β†’ WHERE β†’ GROUP BY β†’ HAVING β†’ SELECT β†’ ORDER BY β†’ LIMIT.

Examples

WHERE vs HAVING

sql
-- WHERE: filter before grouping
-- Only count orders placed in 2025
SELECT customer_id, COUNT(*) AS order_count
FROM   orders
WHERE  created_at >= '2025-01-01'   -- filters rows first
GROUP  BY customer_id;

-- HAVING: filter after grouping
-- Only show customers with more than 5 orders
SELECT customer_id, COUNT(*) AS order_count
FROM   orders
GROUP  BY customer_id
HAVING COUNT(*) > 5;               -- filters groups

Using both WHERE and HAVING together

sql
-- Customers with more than 5 paid orders in 2025
SELECT customer_id, COUNT(*) AS paid_orders
FROM   orders
WHERE  status = 'paid'
  AND  created_at >= '2025-01-01'
GROUP  BY customer_id
HAVING COUNT(*) > 5
ORDER  BY paid_orders DESC;

Pro Tip

A quick mental model: WHERE is for rows, HAVING is for groups. If you catch yourself trying to put `SUM(...)` or `COUNT(...)` in a WHERE clause, that's a sign you need HAVING instead.