Step 1 Understand it
Why you need it. Nobody wants to read 10 million orders. They want "how many?", "how much in total?", "what is the average?". answer those in one line — but the rules quietly change the answers if you don't know them.
New words in this topic (tap one to see what it means)
Watch it happen
Read the animation as text
- An aggregate turns many rows into one value. With no GROUP BY, the whole table (after WHERE) becomes one row.
- COUNT(*) counts rows, whatever is in them: 10.
- COUNT(amount) counts only rows where amount is not NULL: 9. Order 106 has no amount, so it is skipped.
- SUM(amount) adds the 9 known amounts: 793. AVG skips the NULL too, so it is 793 ÷ 9 = 88.11 — not ÷ 10.
- If the question means "per order, counting the missing one as zero", divide by COUNT(*) yourself: 793 ÷ 10 = 79.3. Same data, different question, different answer.
Picture it
flowchart LR A["10 order rows<br/>120, 80, 45, 200, 150,<br/>NULL, 60, 75, 30, 33"] --> C["COUNT(*) = 10"] A --> N["COUNT(amount) = 9<br/>NULL skipped"] A --> S["SUM(amount) = 793"] A --> V["AVG(amount) = 793 / 9"]
The idea
Without , an aggregate squashes the whole (after ) into one row. COUNT(*) counts rows. COUNT(column) counts rows where that column is not NULL. COUNT(DISTINCT column) counts the different non-NULL values. SUM and AVG add and average the non-NULL values. MIN and MAX work on numbers, text (alphabetical) and dates. Because AVG skips NULLs, the average of order amounts is the total divided by 9 orders, not 10.
The shape of it
SELECT COUNT(*), COUNT(column), COUNT(DISTINCT column),
SUM(column), AVG(column), MIN(column), MAX(column)
FROM table_name;Words in lower case are the parts you fill in.
How it works, step by step
- and WHERE decide which rows are in play — here all 10 orders.
COUNT(*)counts the rows: 10.COUNT(amount)counts rows whose amount is not NULL: 9, because order 106 has none.SUM(amount)adds the 9 amounts: 793.AVG(amount)is 793 ÷ 9 = 88.11, not 793 ÷ 10.- If the question means "average per order, counting the missing one as zero", divide SUM by
COUNT(*)instead: 79.3.
Analogy
A cashier at the end of the day. They don't list every sale; they count the receipts (COUNT), add up the takings (SUM), and note the biggest and smallest sale (MAX, MIN). A receipt with the amount torn off still counts as a receipt, but it adds nothing to the takings.
Where the analogy breaks
A cashier would notice the torn receipt and ask about it. doesn't: it silently leaves NULLs out of SUM and AVG, so you have to decide yourself whether a missing value should count as zero.
Worked examples
One summary row for the whole table
SELECT COUNT(*) AS employees,
SUM(salary) AS total_pay,
AVG(salary) AS average_pay,
MIN(salary) AS lowest,
MAX(salary) AS highest
FROM employees;1 row
| employees | total_pay | average_pay | lowest | highest |
|---|---|---|---|---|
| 8 | 510000 | 63750 | 38000 | 95000 |
Three different counts
SELECT COUNT(*) AS all_rows,
COUNT(dept_id) AS with_a_department,
COUNT(DISTINCT dept_id) AS different_departments
FROM employees;1 row
| all_rows | with_a_department | different_departments |
|---|---|---|
| 8 | 7 | 3 |
Gus has no department, so COUNT(dept_id) is 7. The 7 non-NULL values are only 1, 2 and 3, so COUNT(DISTINCT dept_id) is 3.
How NULL changes an average
SELECT COUNT(*) AS orders,
COUNT(amount) AS with_amount,
SUM(amount) AS total,
ROUND(AVG(amount), 2) AS avg_of_known,
ROUND(SUM(amount) * 1.0 / COUNT(*), 2) AS avg_over_all
FROM orders;1 row
| orders | with_amount | total | avg_of_known | avg_over_all |
|---|---|---|---|---|
| 10 | 9 | 793 | 88.11 | 79.3 |
Both averages are "right" — they answer different questions. Say which one you mean.
Must-know points
COUNT(*)counts rows;COUNT(col)counts non-NULL values;COUNT(DISTINCT col)counts different non-NULL values.- SUM, AVG, MIN and MAX ignore NULLs.
- SUM of no rows (or only NULLs) is NULL, not 0. Wrap it:
COALESCE(SUM(x), 0). - Without GROUP BY, an aggregate returns exactly one row — even from an empty table.
- MIN and MAX work on text and dates too.
Common mistakes
- Using
COUNT(column)when you mean "how many rows", and getting a smaller number because of NULLs. - Mixing a plain column with an aggregate without GROUP BY:
SELECT name, MAX(salary) FROM employeesis an error in most ( allows it but the name is unreliable). - Putting an aggregate in WHERE:
WHERE SUM(amount) > 100is an error. That is 's job.
In other databases
- AVG of whole numbers: and SQLite return a decimal; returns a whole number (AVG of 1 and 2 is 1). Use
AVG(x * 1.0)there.
Step 2 Go further (free, optional)
Optional. The notes above are written to be enough on their own. These free resources give a second explanation and more practice.
-
Read
W3Schools — SQL COUNT() Function
short page with a try-it editor, free
- Read
-
Practice
SQLBolt — Lesson 10: Queries with aggregates (Pt. 1)
interactive exercises in the browser, free
Step 3 Practise
Try each one yourself first: out loud, or written and run in the practice box below, which saves every query you run. Then open the answer and compare.
-
ExplainSay what it does and when to use it, in plain words, with no notes.
Explain why
AVG(amount)on orders is 88.11 and not 79.3.Show answer
AVG skips NULLs. There are 10 orders, but order 106 has no amount, so only 9 amounts are averaged. The 9 known amounts add up to £793, and 793 ÷ 9 = 88.11.
79.3 is 793 ÷ 10: it treats the missing amount as £0. That is a different question ("average per order, counting a missing amount as nothing"). If that is what you want, say so:
SUM(amount) * 1.0 / COUNT(*), orAVG(COALESCE(amount, 0)).sqlSELECT COUNT(*) AS orders, COUNT(amount) AS with_amount, SUM(amount) AS total, ROUND(AVG(amount), 2) AS avg_amount FROM orders;1 row
orders with_amount total avg_amount 10 9 793 88.11 -
Writefrom a blank page, then run it
Write one query that returns the number of products, the cheapest price, the dearest price and the average price.
Show answer
Four aggregates in one , with no GROUP BY, give one summary row for the whole table. keeps the average readable: 125 ÷ 7 = 17.857…
sqlSELECT COUNT(*) AS products, MIN(price) AS cheapest, MAX(price) AS dearest, ROUND(AVG(price), 2) AS average_price FROM products;1 row
products cheapest dearest average_price 7 2 40 17.86 -
Predictsay it before you run it
What do
COUNT(*),COUNT(city)andCOUNT(DISTINCT city)return on the customers table?Show answer
6, 5 and 3.
•
COUNT(*)counts rows: 6 sign-ups.
•COUNT(city)counts values that are not NULL: Zoe has no city, so 5.
•COUNT(DISTINCT city)counts different cities: London, Leeds and Bristol, so 3. The NULL is not counted, and the two Londons and two Leeds count once each.sqlSELECT COUNT(*), COUNT(city), COUNT(DISTINCT city) FROM customers;1 row
COUNT(*) COUNT(city) COUNT(DISTINCT city) 6 5 3 -
Edge casesSay what happens with NULLs, ties, duplicates or no rows at all, and fix it.
What does
SELECT SUM(amount), COUNT(*) FROM orders WHERE status = 'refunded';return? Why isn't it 0 and 0?Show answer
One row: NULL and 0.
No order is 'refunded', so the aggregates see no rows. COUNT of nothing is 0. But SUM of nothing is NULL, not 0, because there is nothing to add up. (Without GROUP BY, an aggregate query always returns exactly one row, even when no rows match.)
If a report should show 0, write
COALESCE(SUM(amount), 0).sqlSELECT SUM(amount), COUNT(*) FROM orders WHERE status = 'refunded';1 row
SUM(amount) COUNT(*) NULL 0
Practice box: write and run your answers here
Your queries in this box
Step 4 Interview-ready, then prove it
Your interview answer
About 20 to 40 seconds, said out loud.
Aggregate functions collapse a set of rows into one value: COUNT, SUM, AVG, MIN and MAX. The detail interviewers probe is NULL handling — COUNT star counts rows, COUNT of a column skips NULLs, and SUM and AVG ignore NULLs, so an average is over the known values only. I also remember that SUM over no rows is NULL, so I it to zero when the result feeds a report.
Follow-up questions
What is the difference between COUNT(*), COUNT(1) and COUNT(column)?
COUNT(*) and COUNT(1) both count rows, at the same speed. COUNT(column) counts only rows where that column is not NULL.
What does SUM return when there are no rows?
NULL. COUNT returns 0. Use COALESCE(SUM(x), 0) when you need 0.
Can you use an aggregate in WHERE?
No — WHERE runs before rows are grouped. Filter on aggregates with HAVING, or in an outer query.
Tick each check when you can do it without notes
A topic is finished when you pass all four checks, not when you have read it. Be strict: if you had to look at the notes, it is not ticked yet.
- Explain Say what it does and when to use it, in plain words, with no notes.
- Write Write the query from a blank page, then run it to check.
- Predict Read a query and say which rows it returns before you run it.
- Edge cases Say what happens with NULLs, ties, duplicates or no rows at all, and fix it.
0 of 4 checks ticked.
Saved in this browser. Back it up or move it to another device.