Skip to content

Beginner: asking questions · Aggregation: GROUP BY and HAVING · topic 16 of 62 · 30 min

Aggregate functions

Explanation

An aggregate function squeezes many rows into one value: COUNT counts, SUM adds, AVG averages, MIN and MAX find the smallest and largest. COUNT(*) counts rows, COUNT(column) counts only values that are not NULL, and COUNT(DISTINCT column) counts different values. SUM, AVG, MIN and MAX skip NULLs, which changes averages in ways you must notice.

From rows to answers

So far every query returned one result row per table row. But many questions want a single number: How many students are there? What is the total stock value? What is the highest salary? An aggregate function (from "aggregate", to gather together) reads a whole set of rows and returns one value.

FunctionGivesOn STUDENTS marks
COUNT(*)Number of rows8
COUNT(marks)Number of rows where marks is not NULL7
COUNT(DISTINCT city)Number of different values5 cities
SUM(marks)Total504
AVG(marks)Average (mean) of the non-NULL values72
MIN(marks), MAX(marks)Smallest and largest45 and 91
sql
SELECT COUNT(*) AS all_students, COUNT(marks) AS with_marks, COUNT(DISTINCT city) AS cities
FROM students;
-- 8 | 7 | 5
Sneha has no marks, so COUNT(marks) is one less than COUNT(*).

The three kinds of COUNT

  • COUNT(*) counts rows, whatever is in them. Use it for "how many orders", "how many students".
  • COUNT(column) counts rows where that column is not NULL. COUNT(email) on SHOP customers gives 4, because two have no email.
  • COUNT(DISTINCT column) counts different non-NULL values. COUNT(DISTINCT dept_id) in COMPANY is 4: departments 1 to 4, and Deepak's NULL is not counted.

SUM and expressions

An aggregate can work on an expression, not just a column. The value of all stock is the sum of price × stock over every product:

sql
SELECT SUM(price * stock) AS stock_value FROM products;
-- 169430
The multiplication runs for every row first, then SUM adds the results.

How NULLs are skipped

SUM, AVG, MIN, MAX and COUNT(column) all ignore NULLs. For SUM that is usually what you want. For AVG it needs thought: AVG(marks) divides the total by the number of students who HAVE marks, not by all students.

sql
SELECT ROUND(AVG(marks), 2) AS avg_marks,
       ROUND(AVG(COALESCE(marks, 0)), 2) AS avg_as_zero
FROM students;
-- 72 | 63
504 / 7 = 72, ignoring Sneha. Treating her as 0 gives 504 / 8 = 63. Which is right depends on the question.
Tip: Always round averages and say how in the column name or report: ROUND(AVG(marks), 2). In SQLite, AVG always returns a real number, so ROUND keeps the report tidy.
Careful: SQL Server is different: AVG of an INTEGER column returns an integer, so the fraction is lost (AVG of 1 and 2 is 1). Write AVG(marks * 1.0) or AVG(CAST(marks AS decimal(10,2))) there. PostgreSQL and MySQL return a decimal.

MIN and MAX are not only for numbers

MIN and MAX work on text and dates too. MIN(name) is the first name in alphabetical order; MAX(hire_date) is the most recent hire, because ISO dates sort correctly as text (module 3).

sql
SELECT MIN(salary) AS lowest, MAX(salary) AS highest,
       MIN(hire_date) AS first_hire, MAX(hire_date) AS latest_hire
FROM employees;
-- 45000 | 250000 | 2015-04-01 | 2024-06-01

Aggregates over no rows

If WHERE removes every row, COUNT gives 0 but SUM, AVG, MIN and MAX give NULL. SELECT SUM(price) FROM products WHERE category = 'Toys' returns NULL, not 0. Wrap it in COALESCE(SUM(price), 0) when a report must show 0.

You cannot mix rows and aggregates (yet)

SELECT name, MAX(salary) FROM employees asks for many names and one maximum in the same row. Standard SQL rejects it; SQLite runs it and happens to show the name from the row with the maximum, which hides the problem. To get "who earns the most", sort and LIMIT (module 2) or use a subquery (module 6). To get one answer per group, use GROUP BY, the next topic.

Common mistakes

  • Using COUNT(column) when you mean COUNT(*), and getting a smaller number because of NULLs.
  • Forgetting that AVG skips NULLs. Decide whether missing should count as 0, and use COALESCE if so.
  • Showing NULL instead of 0 when no rows match. Use COALESCE(SUM(x), 0).
  • Writing COUNT(DISTINCT *): it is not valid. Name the column: COUNT(DISTINCT city).
  • Expecting AVG to round. It does not; add ROUND(AVG(x), 2).

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Aggregate functions · 1/7

Aggregate functions

Many questions want one number, not a list of rows. Aggregate functions gather many rows into one value.

Prefer a video by a teacher? Find free videos on “sql aggregate functions count sum avg min max null tutorial” on YouTube ↗

In real life: The headline numbers on a store dashboard

The top strip of an online store's admin dashboard usually shows a few big numbers: total products, units in stock, the value of that stock, and the cheapest and dearest item. Each is one aggregate query over a table. Teams check that the numbers make sense, for example that COUNT(*) matches the product list, before trusting the charts below.

sql
SELECT COUNT(*)            AS products,
       SUM(stock)          AS units,
       SUM(price * stock)  AS stock_value,
       MIN(price)          AS cheapest,
       MAX(price)          AS dearest
FROM products;
SHOP: 8 products, 205 units, stock value 169430, cheapest 160, dearest 2499.

Practice (0/4 done)

Task 1 · Code · SQL

How many students are there, and how many have marks recorded? Return two columns: all_students and with_marks.

Task 2 · Code · SQL

Return the average marks rounded to 2 decimal places, named avg_marks, and the average when a missing mark counts as 0, also rounded to 2, named avg_as_zero.

Task 3 · Code · SQL

What is all the stock worth? Return one column, stock_value: the sum of price * stock over all products.

Task 4 · Code · SQL

From employees (COMPANY), return the lowest and the highest monthly salary, named lowest and highest.

Quick quiz

Question 1

1.A column has the values 10, NULL, 20. What is AVG of it?

Question 2

2.customers has 6 rows and 2 have no email. What does COUNT(email) return?

Question 3

3.No product is in the Toys category. What does SELECT SUM(price) FROM products WHERE category = 'Toys' return?

Question 4

4.Which counts the number of different cities, ignoring NULL?

Question 5

5.In SQL Server, an INTEGER column holds 1 and 2. What does AVG return?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A column has the values 10, NULL, 20. What is AVG of it?

Answer: 15

AVG skips NULL: (10 + 20) / 2 = 15.

customers has 6 rows and 2 have no email. What does COUNT(email) return?

Answer: 4

COUNT(column) counts only values that are not NULL.

No product is in the Toys category. What does SELECT SUM(price) FROM products WHERE category = 'Toys' return?

Answer: NULL

SUM over no rows is NULL (COUNT would be 0). Use COALESCE(SUM(price), 0) to show 0.

Which counts the number of different cities, ignoring NULL?

Answer: COUNT(DISTINCT city)

DISTINCT goes inside the brackets of COUNT.

In SQL Server, an INTEGER column holds 1 and 2. What does AVG return?

Answer: 1

SQL Server returns an integer average for integer input. Use AVG(col * 1.0) there.

A table has 10 rows; the email column is NULL in 3 of them. What is COUNT(email)?Related

Answer: 7

COUNT(column) counts only rows where the column has a value: 10 - 3 = 7.

From Counting and totals: aggregate functions

Why does PostgreSQL reject SELECT city, name, SUM(amount) FROM t GROUP BY city?Related

Answer: name is neither grouped nor aggregated, so one city has many possible names

Each output row is one group. A column must be the same for the whole group (grouped) or combined from it (aggregated).

From GROUP BY and HAVING

students has 8 rows in 5 different cities. How many rows does SELECT city, COUNT(*) FROM students GROUP BY city return?Related

Answer: 5

GROUP BY gives one row per group, and there are 5 cities.

From GROUP BY

employees has 11 rows. How many rows does SELECT name, AVG(salary) OVER (PARTITION BY dept_id) FROM employees return?Related

Answer: 11

A window function never removes rows; each of the 11 rows gets its department average.

From OVER and PARTITION BY

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next: GROUP BY →

Next steps

  1. 4 more topics in “Aggregation: GROUP BY and HAVING” before its exam: GROUP BY, HAVING versus WHERE, Conditional aggregation and string aggregation, Writing reports with aggregation.
  2. Module 4 exam: Aggregation, GROUP BY and HAVING · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, JOINs. 62 topics left in the course before the final exam.

← Previous: NULL: the missing value