build-a-sql-database / lesson-31.md
Lesson 31 · Aggregates, joins & persistence

COUNT(*)

Aggregates collapse many rows into one summary value. Today you add the first and simplest, COUNT(*), which returns how many rows a query produced.

The goal

Parse and execute COUNT(*) so a SELECT returns a single row holding the row count.

Start here - the target
TO DO
Scenario: Counting rows with COUNT(*)
Givena users table with three rows
When"SELECT COUNT(*) FROM users" is executed
Thenthe result is a single row holding the integer 3
And"SELECT COUNT(*) FROM users WHERE age = 30" returns 2
Background

An aggregate is a function that folds a whole set of rows into a single value, and COUNT(*) is the gateway drug: it simply reports how many rows the query produced. Represent an aggregate select item as a small function + column argument pair (here the column is just *) rather than a one-off boolean flag - that shape is exactly what SUM(age) and MAX(age) will reuse tomorrow, so building it now keeps the next lesson to pure execution. Execution, after the filter runs, returns a one-row result whose single value is the count of surviving rows.

The key shift is that an aggregate query returns one row summarizing many, not one row per input row - a different shape of result than everything so far. Notice it composes with WHERE for free: filter first, then count what is left, so COUNT(*) ... WHERE age = 30 counts only the matching rows. This one-row-out pattern is the template for SUM, MIN, MAX, and AVG next.

Make it work
// represent a select item as an aggregate call, general enough to grow:
type Agg struct { Func string; Column string } // e.g. {"COUNT","*"}
// parse COUNT ( * ) into Agg{Func:"COUNT", Column:"*"}
// execute: after filtering, if the select is an aggregate,
// return one row whose value is len(filtered rows)
CheckpointDONE
A query can count its rows with COUNT(*). Commit and stop here.