field guide❋ Page 1 of 14 palette size
SQL · Garden
where the day's harvest gets sorted
❋
— Chapter —
V

The Sorting Table

in which the gardener tips the day's basket onto a wooden table
and arranges what came home into smaller baskets, one per kind
❋
Summer · Sorting Table · Chapter V of IX

Page 2 · Welcome

The day's basket comes back. Now what?

questions that can't be answered by one flower at a time

In Chapter I you walked the rose_bed and picked individual roses. In Chapter IV you visited a second bed — the flowers table, with its Marigold, Jasmine, Lotus, and Tulip — and paired each bloom with a bee. Today the gardener returns to that same bed with a fuller trait card: color and height have joined id and species, and the table has been renamed to flower_bed for its expanded shape — with more flowers, too, than the four you first met. Each question you've asked so far was about individual rows — "the tallest flower," "which bee visits which flower."

But many garden questions aren't about individual flowers at all. They're about the whole basket. How many flowers came home today? What's the average height? Which color has the most blooms? These all require looking at many rows at once and reducing them to a single number, or to a number per group.

That's what this chapter is about. The gardener returns from the flower bed, walks out the back door, and tips the basket onto the sorting table — where flowers get arranged into smaller baskets by kind, and where you can ask things like "how many?" and "on average, how tall?"

the sorting table, just before sorting begins

Page 3 · The bigger harvest

A bigger harvest

ten flowers, four kinds

Today's harvest is bigger than the four flowers you met in Chapter IV. Ten flowers came home, of four different kinds — Marigold, Jasmine, Lotus, and Tulip. Each flower has the same trait card you saw on Chapter I's rose_bed — id, species, color, height — now applied to the flower_bed.

flower_bed
idspeciescolorheight
1Marigoldorange18
2Jasminewhite14
3Lotuspink24
4Tulippurple16
5Lotuspink20
6Tulippink22
7Tulippurple30
8Jasminepurple28
9Lotuspink15
10Lotuspink∅ (null)
Ten flowers. Four colors — pink ×5, purple ×3, white ×1, orange ×1. Notice color doesn't line up with species: the pink basket holds four Lotuses and a Tulip; the purple basket holds two Tulips and a Jasmine — so grouping by color and grouping by species tell different stories.

Take a moment to scan it. Notice: pink is the most common color, with five flowers — four Lotuses and a Tulip. White and orange tie as the rarest, one flower each. The tallest flower — id 7, a purple Tulip — reaches 30 cm. And Flower 10 — a Lotus — came in without its height recorded; that empty cell is a NULL, and it will matter when we start summing and averaging. These are the kinds of facts the sorting table makes easy to extract.

Page 4 · COUNT(*)

The simplest count

how many flowers came home today?

Before sorting, the simplest question: how many flowers are in the basket? SQL has a function for this: COUNT(*). The * means "every row" — count them all, regardless of what's in any particular column.

Notice the result is a single row, with a single number. No bed visualization needed — just the answer.

sorting-table.sql
the sorting table
COUNT(*)
Run the query. One number comes back.
Field guide: COUNT(*) is the first aggregate function — a function that takes many rows in and produces one number out. Add a WHERE first if you want to count only some rows.

Page 5 · The aggregate family

The aggregate family

SUM, AVG, MIN, MAX

COUNT has siblings. Where COUNT just counts the rows, the others each do something with the values in a single column:

  • SUM(height) — add all the heights together
  • AVG(height) — the average
  • MIN(height) — the shortest
  • MAX(height) — the tallest
  • COUNT(height) — how many rows have a height recorded (NULLs are skipped, unlike COUNT(*))

You can ask all of them at once. Each one becomes a column in the result.

sorting-table.sql
the sorting table
aggregates
Five numbers, one row. The whole bed reduced to summary statistics.
Field guide: aggregate functions ignore NULL values. Try the count vs count(height) chip: COUNT(*) counts every row (all ten), but COUNT(height) counts only rows with a recorded height (nine). SUM and AVG likewise compute over the nine, not ten.

Page 6 · Sorting the harvest

Sorting into smaller baskets

one big number isn't enough

SUM, AVG, MIN, MAX over the whole bed are useful — but you usually want them per color. "What's the average height of pink flowers, compared to purple?" "How many of each color came home?"

That's where the sorting table earns its name. The gardener takes each flower out of the big basket and drops it into a smaller basket labeled with its color. By the end, you have one small basket per color, each with its own count, its own average, its own tallest and shortest.

pink purple white orange one basket per color · ten flowers became four groups
After sorting: four baskets, each holding the flowers of one color.

SQL's word for "sort flowers into a basket by some trait" is GROUP BY. The clause comes right after WHERE and tells the database which column to use as the basket label.

Page 7 · GROUP BY

GROUP BY color

one row per color, not one row per flower

Watch what happens when you add GROUP BY color: the result changes from "ten rows, one per flower" to "four rows, one per color." Each row in the result represents one of the small baskets on the sorting table.

Crucially, every column you SELECT must either be the thing you grouped by, or an aggregate over the group. You can't ask for species here — a basket grouped by color has flowers of various species, no single value to name for the whole basket.

sorting-table.sql
the sorting table
GROUP BY color
Four baskets appear on the table — one per color.
Field guide: once you GROUP BY, the result has one row per group, not one row per original flower. The individual flowers still exist — they're just gathered into baskets you can ask questions of.

Page 8 · COUNT per group

Counts, per basket

how many in each basket?

The previous query already showed counts per color. Here it is again with a tiny tweak: let's arrange the result so the most-common color comes first. Aggregate functions work in ORDER BY too.

sorting-table.sql
the sorting table
COUNT(*) DESC
The fullest basket comes first.
Field guide: ORDER BY COUNT(*) DESC arranges the result baskets from biggest to smallest. You can ORDER BY any aggregate, not just COUNT.

Page 9 · AVG, MIN, MAX per group

Statistics, per basket

average, tallest, shortest — by color

The real power of GROUP BY is asking summary questions per basket. The average height per color. The tallest flower in each color. All in one query.

sorting-table.sql
the sorting table
AVG / MIN / MAX
Each basket shows its statistics. The purples are tallest on average; whites the shortest.
Notice: each row in the result is one basket, with its statistics computed only from the flowers in that basket. The pink basket's AVG is the average of pink heights, not all heights.

Page 10 · HAVING

HAVING: filter the baskets

"only show me baskets with more than one flower"

WHERE filters flowers before they reach the table. HAVING filters baskets after they've been sorted. The two look similar in syntax but apply at different stages.

Two baskets — orange and white — hold just one flower each (the lone Marigold, and a single Jasmine). If you only care about colors with multiple flowers, add a HAVING clause that filters by the basket's COUNT:

sorting-table.sql
the sorting table
HAVING COUNT(*) > 1
The orange and white baskets are filtered out — one flower each.
Field guide: WHERE happens before sorting — it picks which flowers go to the table. HAVING happens after sorting — it picks which baskets to show. The condition in HAVING can use aggregates; the condition in WHERE cannot.

Page 11 · The anatomy of an aggregate query

The full anatomy

seven clauses, all in order

An aggregate query strings together every clause you saw in Chapter I — plus the new ones from this chapter. The clauses are still strictly ordered. The new ones — GROUP BY and HAVING — slot in between WHERE and ORDER BY.

SELECT
which columns and aggregates to return per group
FROM
which table(s) to read
WHERE
which flowers to keep before sorting
GROUP BY
which column to sort the flowers by
HAVING
which baskets to keep after sorting
ORDER BY
how to arrange the result baskets
LIMIT
how many to keep
SELECT color, COUNT(*), AVG(height)
FROM flower_bed
WHERE height > 15
GROUP BY color
HAVING COUNT(*) > 1
ORDER BY AVG(height) DESC
LIMIT 2;
A full aggregate query, every clause in its proper place.

Page 12 · Sort it yourself

Sort the harvest yourself

write any aggregate query you like

The flower_bed is still on the table. Try any aggregate query. The result table below shows one row per basket — or one row per flower, if you leave GROUP BY out.

scratch.sql
the sorting table
your query
Edit the query, then run.
⌘/Ctrl + Enter to run
Result —
Run a query to see rows here.

flower_bed — schema

idinteger
speciestext
colortext · pink, purple, white, orange
heightinteger · cm · may be NULL
Try: SELECT MAX(height) FROM flower_bed; · SELECT color, COUNT(*) FROM flower_bed GROUP BY color HAVING COUNT(*) >= 3; · SELECT color, MIN(height), MAX(height) FROM flower_bed GROUP BY color;

Page 13 · End of Chapter V

The baskets come home

what you've learned, and what's next

In the garden of the Sorting Table, the day's basket is tipped out at last. The flowers are gathered into smaller baskets — one for each color — and from each smaller basket a number is taken: how many, how tall on average, what the tallest and shortest were.

COUNT(*)
how many rows in each group
SUM(col)
add the values
AVG(col)
the average
MIN(col) / MAX(col)
the smallest / largest
GROUP BY col
sort the flowers into baskets by that column
HAVING expr
filter the baskets after sorting (aggregates allowed)

That closes Summer. Beyond it lies Autumn, with three further gardens. The first is called the Basket Inside the Basket: in it, a smaller basket is gathered first, then used inside a larger one. The second is called the Directory at the Gate: there the gardener finds any flower without walking every bed. The third is called Looking at Neighbors: each flower, standing in its own row, can see how it compares to the flowers around it. For today, the baskets are sorted, the table is wiped clean, and the sun lowers.

Check your basket

Two questions to confirm GROUP BY clicks. Write a query and press Check.

1. Count the flowers by color. Return color and the count, ordered most common first.

SELECT color, COUNT(*) FROM flower_bed GROUP BY color ORDER BY COUNT(*) DESC;

2. Find colors whose tallest flower is at least 25 cm. Show color and MAX(height).

SELECT color, MAX(height) FROM flower_bed GROUP BY color HAVING MAX(height) >= 25;
· Sunset ·
❋
end of Chapter
V
the table is wiped, the baskets stacked —
one small basket set inside a larger one
❋
an intermission is recommended · Chapter VI awaits