Aggregate Functions
Summarise many rows into one value with COUNT, SUM, AVG, MAX and MIN — and learn exactly how each one treats NULL.
How many innings were played this season? What is the total of all runs? What was the best score? Questions like these do not want a list of rows — they want one number that summarises many rows. That is the job of aggregate functions.
All examples in this chapter use the innings table — 12 innings by 4 players. Look closely: it has ties (73, 59 and 45 each appear twice) and one NULL (MS Dhoni did not bat on 2025-03-31). Both matter a lot in this chapter.
| id | player | team | match_date | runs |
|---|---|---|---|---|
| 1 | Virat Kohli | RCB | 2025-03-22 | 82 |
| 2 | Virat Kohli | RCB | 2025-03-29 | 59 |
| 3 | Virat Kohli | RCB | 2025-04-05 | 73 |
| 4 | Rohit Sharma | MI | 2025-03-23 | 45 |
| 5 | Rohit Sharma | MI | 2025-03-30 | 76 |
| 6 | Rohit Sharma | MI | 2025-04-06 | 0 |
| 7 | MS Dhoni | CSK | 2025-03-24 | 26 |
| 8 | MS Dhoni | CSK | 2025-03-31 | NULL |
| 9 | MS Dhoni | CSK | 2025-04-07 | 45 |
| 10 | Jos Buttler | RR | 2025-03-25 | 73 |
| 11 | Jos Buttler | RR | 2025-04-01 | 59 |
| 12 | Jos Buttler | RR | 2025-04-08 | 107 |
Definition
An aggregate function performs a calculation on a set of values (many rows) and returns a single summary value. Except for
COUNT(*), aggregate functions ignore NULL values.
- Set of values — the input is a whole column (or a group of rows, see GROUP BY), not one cell.
- Single summary value — 12 rows go in, 1 value comes out. The detail rows are "collapsed".
- Ignore NULL — a NULL means "unknown / missing", so SUM, AVG, MIN, MAX and COUNT(column) simply skip it. It is not treated as 0.
The five core aggregate functions
| Function | What it returns | Works on | NULL handling |
|---|---|---|---|
COUNT(*) | number of rows | any row | counts every row, even all-NULL rows |
COUNT(column) | number of non-NULL values in the column | any data type | skips NULL |
SUM(column) | total of all values | numbers only | skips NULL |
AVG(column) | average = SUM ÷ COUNT(column) | numbers only | skips NULL (they are not in the divisor either) |
MAX(column) | highest value | any type (numbers, text, dates) | skips NULL |
MIN(column) | lowest value | any type (numbers, text, dates) | skips NULL |
Syntax
sql
SELECT
AGGREGATE_FUNCTION(column_name) AS alias
FROM table_name
[WHERE condition];| Part | Meaning |
|---|---|
AGGREGATE_FUNCTION | one of COUNT, SUM, AVG, MAX, MIN |
column_name | the column whose values are summarised (* only for COUNT) |
AS alias | a readable name for the result column — always give one |
WHERE | optional; filters rows before they are aggregated |
Example — summarise the whole table
sql
SELECT
COUNT(*) AS innings,
SUM(runs) AS total,
AVG(runs) AS average,
MAX(runs) AS best,
MIN(runs) AS lowest
FROM innings;| innings | total | average | best | lowest |
|---|---|---|---|---|
| 12 | 645 | 58.63636363636363 | 107 | 0 |
How it works: 12 rows collapse into one row. SUM adds the 11 known scores = 645. AVG = 645 ÷ 11 (not 12!) because Dhoni's NULL is ignored in both the total and the count. MIN is 0 (Rohit's duck) — 0 is a real value, NULL is not.
Note — SQL Server integer average:
runsis anINTcolumn. In SQL Server,AVGof an integer column returns an integer, so the query above shows 58, not 58.636 (the playground, which uses SQLite underneath, shows the decimal). To get the decimal average in SQL Server, convert first:AVG(runs * 1.0)orAVG(CAST(runs AS DECIMAL(10,2))). From now on this chapter writesAVG(runs * 1.0)so both engines give the same answer.
Example — COUNT(*) vs COUNT(column) vs COUNT(DISTINCT)
sql
SELECT
COUNT(*) AS all_rows,
COUNT(runs) AS with_runs,
COUNT(DISTINCT runs) AS distinct_scores,
COUNT(DISTINCT player) AS players
FROM innings;| all_rows | with_runs | distinct_scores | players |
|---|---|---|---|
| 12 | 11 | 8 | 4 |
COUNT(*)counts rows → 12.COUNT(runs)counts non-NULL values → 11 (Dhoni's NULL is skipped).COUNT(DISTINCT runs)counts different non-NULL values → 8 (82, 59, 73, 45, 76, 0, 26, 107 — the ties count once).
Example — MIN and MAX on text and dates
sql
SELECT
MIN(player) AS first_name_az,
MAX(player) AS last_name_az,
MIN(match_date) AS first_match,
MAX(match_date) AS last_match
FROM innings;| first_name_az | last_name_az | first_match | last_match |
|---|---|---|---|
| Jos Buttler | Virat Kohli | 2025-03-22 | 2025-04-08 |
For text, MIN/MAX use alphabetical order; for dates, earliest / latest. SUM and AVG would fail on these columns — they need numbers.
Aggregates on an empty set
If no rows match (e.g. WHERE team = 'KKR' — KKR has no innings), COUNT(*) returns 0, but SUM, AVG, MIN, MAX return NULL (there is nothing to add or compare). Wrap with ISNULL(SUM(runs), 0) if you want 0.
Common mistakes
- ❌ Thinking
AVG(runs)= total ÷ number of rows → ✅ it is total ÷ non-NULL values (645 ÷ 11, not 645 ÷ 12). - ❌ Using
COUNT(runs)to count rows → ✅ useCOUNT(*);COUNT(column)silently drops NULLs. - ❌ Mixing aggregates and plain columns:
SELECT player, SUM(runs) FROM innings→ ✅ addGROUP BY player(next topic). - ❌
WHERE SUM(runs) > 100→ ✅ aggregates cannot be used in WHERE; filter groups withHAVING. - ❌ Forgetting integer division in SQL Server:
AVG(int_column)→ ✅AVG(int_column * 1.0).
SQL Server vs MySQL / PostgreSQL
| Behaviour | SQL Server | MySQL | PostgreSQL |
|---|---|---|---|
AVG of an INT column | integer (58) | decimal (58.6364) | decimal (58.636…) |
| Very large counts | COUNT_BIG(*) returns BIGINT | COUNT(*) is already BIGINT | COUNT(*) is BIGINT |
Interview tip
Q: What is the difference between COUNT(*), COUNT(1) and COUNT(column)?
A: COUNT(*) and COUNT(1) both count all rows (no performance difference in modern engines). COUNT(column) counts only rows where that column is not NULL. On our table: 12, 12 and 11.
Key points to remember
- An aggregate function turns many rows into ONE summary value: COUNT, SUM, AVG, MAX, MIN.
- All aggregates ignore NULL — except COUNT(*), which counts every row.
- COUNT(*) = number of rows (12); COUNT(runs) = number of non-NULL values (11); COUNT(DISTINCT runs) = number of different values (8).
- AVG = SUM ÷ COUNT(column): Dhoni's NULL is left out of both, so 645 ÷ 11 = 58.64.
- SUM and AVG need numbers; MAX and MIN work on numbers, text (A–Z) and dates.
- In SQL Server AVG of an INT column returns an INT (58) — use AVG(runs * 1.0) for decimals.
- On an empty set COUNT returns 0 but SUM/AVG/MIN/MAX return NULL.
Try it yourself
Season summary in one row
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
COUNT(*) vs COUNT(column) vs COUNT(DISTINCT)
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Aggregates on an empty set
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Practice: Virat Kohli's season
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Revision Flashcards
Tap a card to flip it and see the answer.
