One summary per group with GROUP BY
Aggregates collapse a whole table into one number. GROUP BY goes one step further: it splits rows into buckets first, then computes one aggregate per bucket. One number per country, per day, per category — the breakdown behind every dashboard chart.
The code
Station counts per country, for stations west of 100°W:
SELECT country, COUNT(*) AS num_stations FROM weather_stations WHERE lon < -100.0 GROUP BY country;
The execution order matters:
WHERE lon < -100.0— filter rows first.GROUP BY country— bucket the survivors by country.COUNT(*)— tally each bucket. One result row per country.
Every column in the SELECT must either be in the GROUP BY or wrapped in an aggregate — otherwise the database can't know which row's value you mean.
In practice
This is the single most useful query shape in analytics: revenue per month, errors per service, users per plan. If you've ever written .groupby() in pandas after pulling a whole table, this is the same operation — pushed into the database where it belongs.
Python bridge: df[df.lon < -100].groupby('country').size(). Filter, group, summarize — identical verbs, identical order, in both languages.
Try it
Practice question: using this sample of weather_stations, predict the result of the query above. Then add AVG(elevation) AS avg_elevation to the SELECT — how does the result change?
| country | lon | elevation |
|---|---|---|
| USA | -110.2 | 1500 |
| USA | -105.5 | 800 |
| Canada | -120.0 | 600 |
| Mexico | -99.5 | 200 |
| USA | -115.0 | 2100 |
Show answer
SELECT country, COUNT(*) AS num_stations FROM weather_stations WHERE lon < -100.0 GROUP BY country;
Two rows: USA → 3 stations, Canada → 1. Mexico never makes it to grouping — its lon of -99.5 fails the WHERE filter, so it's gone before any bucket exists.
SELECT country, COUNT(*) AS num_stations,
AVG(elevation) AS avg_elevation
FROM weather_stations
WHERE lon < -100.0
GROUP BY country;
| country | num_stations | avg_elevation |
|---|---|---|
| USA | 3 | 1466.7 |
| Canada | 1 | 600.0 |
Same two rows, one extra column — the shape doesn't change, the summary per group just gets richer. USA's average elevation is (1500 + 800 + 2100) / 3 = 1466.7; Canada's is 600.
Further reading
GROUP BY in SQL: How to Aggregate and Analyze Data Efficiently
← All posts