GROUP BY collapses rows into summarized groups, and HAVING filters those groups after they're formed.
Groups rows that share a value so aggregate functions can summarize each group.
SELECT column, AGG(column)
FROM table_name
GROUP BY column;SELECT department, COUNT(*)
FROM Employee
GROUP BY department;| department | count |
|---|---|
| IT | 12 |
| HR | 5 |
Common Mistake
Selecting a column that's neither grouped nor wrapped in an aggregate function — most engines reject this outright.
Filters grouped results, the way WHERE filters individual rows.
SELECT column, AGG(column)
FROM table_name
GROUP BY column
HAVING condition;SELECT department, COUNT(*)
FROM Employee
GROUP BY department
HAVING COUNT(*) > 5;Common Mistake
Using WHERE to filter on an aggregate value — that's HAVING's job specifically, because the aggregate doesn't exist yet at the point WHERE runs.
Grouping and filtering set up the last piece of core querying: combining data from more than one table at once, with joins.