Aggregation & GROUP BY
Concept  ·  SQL & Querying

WHERE filters rows before grouping; HAVING filters groups after — a misplaced HAVING filters the wrong thing.

Same logical-order story as the window-in-WHERE atom, one floor down: at WHERE time, groups don't exist, so a group property is unaskable — hence a binder error, not a wrong answer. The performance corollary: conditions on raw rows belong in WHERE even when HAVING would accept them, because WHERE prunes before the group work and HAVING after.

See it break
rides
pilotpassengers
ana4
ana2
bo1
The trap
SELECT
  pilot,
  SUM(passengers) FROM
  rides
WHERE
  SUM(passengers) > 3
GROUP BY
  pilot
ORDER BY
  pilot;
The fix
SELECT
  pilot,
  SUM(passengers) AS total
FROM
  rides
GROUP BY
  pilot
HAVING
  SUM(passengers) > 3
ORDER BY
  pilot;

Filtering SUM(passengers) in WHERE raised a binder error — at WHERE time the groups don't exist yet, so an aggregate is unaskable; it belongs in HAVING.

The rule

The two-clause split by what's being tested — rows → WHERE, group properties → HAVING; never row-filters in HAVING.

Shows up elsewhere
Top-N per groupCOUNT(*) vs COUNT(col) vs COUNT(DISTINCT col)
Try one →Prove it in practice →