SQL / CHAPTER 07

Aggregate at the right grain

Intermediate

Summarize groups without double-counting or mixing row levels.

By the end of this chapter

  • State what one output row represents
  • Distinguish WHERE from HAVING
  • Count matched children correctly after a left join

The idea

Aggregation changes the grain of a result: several input rows become one summary row. State that grain explicitly, such as one row per team, before selecting expressions. GROUP BY defines the groups. WHERE filters individual input rows before aggregation, while HAVING filters the groups after aggregate values are available. Mixing these stages often answers a different question.

After a left join, COUNT(*) includes the preserved parent row even when no child matched. Counting a non-NULL child key instead yields zero for that case. Avoid selecting arbitrary non-grouped columns in a grouped report, even though SQLite permits some forms: the value may not represent the intended row. Consider join multiplication before SUM, and test groups with zero, one, and several matching items.

SQL example
WITH sales(team,amount) AS (VALUES('A',4),('A',6),('B',3))
SELECT team, SUM(amount) AS total
FROM sales GROUP BY team
HAVING SUM(amount) >= 5
ORDER BY team;
Result
team | total
A    | 10

Aggregation produces totals of ten and three. HAVING then retains only the group whose total reaches the threshold.

Try it yourself

Report every team’s number of open tasks, including zero, with one output row per team.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

Open SQL coding challenges

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Which clause filters groups by COUNT(*) > 2?