SQL exercises with solutions: COUNT, SUM, AVG, MIN, MAX
Updated on
Aggregates sum up many rows into one value: COUNT, SUM, AVG, MIN, MAX. These 3 exercises range from level 1 to level 2; they use the syntax of SQLite, SpeedQL’s SQL engine.
Tip: COUNT(*) counts rows; COUNT(column) skips NULL values.
Read the “COUNT, SUM, AVG, MIN, MAX” card in the cheat sheet
Exercise 1
Show the total number of products.
| id | name | category | price | stock |
|---|---|---|---|---|
| 1 | Keyboard | Office | 25 | 40 |
| 2 | Mouse | Office | 15 | 0 |
| 3 | Screen | Display | 180 | 12 |
| 4 | Headset | Audio | 60 | 8 |
| 5 | Webcam | Office | 45 | 0 |
| 6 | Speaker | Audio | 35 | 25 |
Show the hint
Topics to use: COUNT, SUM, AVG, MIN, MAX.
Query structure:
SELECT COUNT(*)
FROM …Show the solution
SELECT COUNT(*)
FROM products;Expected result (1 row):
| COUNT(*) |
|---|
| 6 |
Exercise 2
Show the highest salary among all employees.
| id | name | department | salary |
|---|---|---|---|
| 1 | Alice | Finance | 32000 |
| 2 | Bob | IT | 41000 |
| 3 | Claire | Finance | 38000 |
| 4 | David | HR | 29000 |
| 5 | Emma | IT | 50000 |
| 6 | Farid | IT | 47000 |
| 7 | Gaelle | HR | 33000 |
| 8 | Hugo | Finance | 44000 |
Show the hint
Topics to use: COUNT, SUM, AVG, MIN, MAX.
Query structure:
SELECT MAX(…)
FROM …Show the solution
SELECT MAX(salary)
FROM employees;Expected result (1 row):
| MAX(salary) |
|---|
| 50000 |
Exercise 3
Show the number of draws (both teams scored the same number of goals) and the total number of goals scored in those matches.
| id | played_on | home_id | away_id | home_goals | away_goals |
|---|---|---|---|---|---|
| 1 | 2025-08-02 | 1 | 2 | 2 | 1 |
| 2 | 2025-08-03 | 3 | 4 | 0 | 0 |
| 3 | 2025-08-09 | 5 | 1 | 1 | 3 |
| 4 | 2025-08-10 | 2 | 3 | 2 | 2 |
| 5 | 2025-08-16 | 4 | 5 | 3 | 1 |
| 6 | 2025-08-17 | 1 | 3 | 1 | 0 |
| 7 | 2025-08-23 | 2 | 4 | 0 | 1 |
| 8 | 2025-08-24 | 3 | 5 | 2 | 3 |
| 9 | 2025-08-30 | 4 | 1 | 1 | 1 |
| 10 | 2025-08-31 | 5 | 2 | 0 | 2 |
Show the hint
Topics to use: WHERE (filters), COUNT, SUM, AVG, MIN, MAX.
Query structure:
SELECT COUNT(*), SUM(… + …)
FROM …
WHERE … = …Show the solution
SELECT COUNT(*), SUM(home_goals + away_goals)
FROM matches
WHERE home_goals = away_goals;Expected result (1 row):
| COUNT(*) | SUM(home_goals + away_goals) |
|---|---|
| 3 | 6 |
More exercises: level 5 of the SQL course, “Summarising a table: aggregates”
These 5 exercises come from level 5 of the course, on other tables. Each one has written hints and its solution, folded by default.
Exercise 4
How many books are there in the books table?
| id | title | author_id | genre | pages | year |
|---|---|---|---|---|---|
| 1 | Cold River | 1 | Novel | 320 | 2011 |
| 2 | Salt Roads | 2 | Travel | 210 | 2016 |
| 3 | The Glass Hive | 3 | Sci-Fi | 412 | 2019 |
| 4 | Winter Ledger | 1 | Crime | 288 | 2014 |
| 5 | Desert Letters | 2 | Novel | 356 | 2020 |
| 6 | Small Engines | 4 | Sci-Fi | 198 | 2022 |
| 7 | Harbor Lights | 5 | Novel | 445 | 2008 |
| 8 | Night Garden | 3 | Crime | 301 | 2017 |
| 9 | Paper Birds | 5 | Poetry | 96 | 2012 |
| 10 | Open Maps | 4 | Travel | 240 | 2021 |
Show the hints
Hint: SELECT COUNT(*) FROM books;
Show the solution
SELECT COUNT(*) AS nb_books
FROM books;Expected result (1 row):
| nb_books |
|---|
| 10 |
COUNT(*) counts every row of the table.
Exercise 5
How many seats were sold in total, across all flights?
| id | airline | origin | dest | departs | duration_min | price | seats_sold | capacity |
|---|---|---|---|---|---|---|---|---|
| 1 | SkyJet | CDG | MAD | 2025-06-01 08:10 | 125 | 89 | 150 | 180 |
| 2 | AirNova | CDG | LIS | 2025-06-01 11:40 | 155 | 120 | 160 | 170 |
| 3 | BlueWing | LYS | FCO | 2025-06-02 07:30 | 95 | 75 | 110 | 150 |
| 4 | SkyJet | MAD | CDG | 2025-06-02 18:20 | 120 | 95 | 170 | 180 |
| 5 | AirNova | BER | CDG | 2025-06-03 09:05 | 110 | 105 | 140 | 160 |
| 6 | BlueWing | NCE | BER | 2025-06-03 13:50 | 130 | 140 | 90 | 150 |
| 7 | SkyJet | CDG | FCO | 2025-06-04 06:45 | 135 | 99 | 175 | 180 |
| 8 | AirNova | LIS | MAD | 2025-06-04 16:15 | 75 | 65 | 60 | 120 |
| 9 | BlueWing | FCO | LYS | 2025-06-05 20:30 | 100 | 82 | 130 | 150 |
| 10 | SkyJet | CDG | BER | 2025-06-05 10:00 | 105 | 110 | 120 | 180 |
| 11 | AirNova | MAD | LIS | 2025-06-06 12:25 | 80 | 70 | 95 | 120 |
| 12 | BlueWing | LYS | MAD | 2025-06-06 15:40 | 115 | 79 | 100 | 150 |
Show the hints
Hint 1 · lead: You need to add up a column over all the rows.
Hint 2 · method: Add up the seats_sold column with SUM.
Show the solution
SELECT SUM(seats_sold) AS total_seats
FROM flights;Expected result (1 row):
| total_seats |
|---|
| 1500 |
SUM adds up the column over all the rows.
Exercise 6
Show the date of the first loan (first_loan) and the date of the last loan (last_loan).
| id | book_id | member_id | loan_date | return_date |
|---|---|---|---|---|
| 1 | 1 | 1 | 2025-01-05 | 2025-01-19 |
| 2 | 3 | 2 | 2025-01-10 | 2025-02-02 |
| 3 | 5 | 1 | 2025-02-01 | 2025-02-10 |
| 4 | 7 | 3 | 2025-02-03 | NULL |
| 5 | 3 | 4 | 2025-02-15 | 2025-03-01 |
| 6 | 2 | 2 | 2025-03-02 | 2025-03-30 |
| 7 | 8 | 5 | 2025-03-05 | NULL |
| 8 | 1 | 3 | 2025-03-10 | 2025-03-18 |
| 9 | 6 | 1 | 2025-03-20 | 2025-04-15 |
| 10 | 3 | 5 | 2025-04-01 | NULL |
| 11 | 9 | 4 | 2025-04-05 | 2025-04-12 |
| 12 | 10 | 2 | 2025-04-08 | 2025-04-20 |
Show the hints
Hint 1 · lead: MIN and MAX are not only for numbers.
Hint 2 · method: MIN(loan_date) AS first_loan and MAX(loan_date) AS last_loan.
Show the solution
SELECT MIN(loan_date) AS first_loan, MAX(loan_date) AS last_loan
FROM loans;Expected result (1 row):
| first_loan | last_loan |
|---|---|
| 2025-01-05 | 2025-04-08 |
In the YYYY-MM-DD format, alphabetical order is also chronological order: MIN gives the oldest date, MAX the most recent.
Exercise 7
How many contacts have an email address?
| id | name | phone | city | |
|---|---|---|---|---|
| 1 | Alice Martin | alice@mail.com | 0612345678 | Paris |
| 2 | bob durand | NULL | 0698765432 | Lyon |
| 3 | CLAIRE ROUX | claire@work.org | NULL | NULL |
| 4 | David Lefevre | NULL | NULL | Nantes |
| 5 | Emma Petit | emma@mail.com | 0611223344 | NULL |
Show the hints
Hint 1 · lead: One of the two forms of COUNT ignores empty cells.
Hint 2 · method: COUNT(email) only counts the non-empty emails.
Show the solution
SELECT COUNT(email) AS with_email
FROM contacts;Expected result (1 row):
| with_email |
|---|
| 3 |
COUNT(*) would have given 5; COUNT(email) ignores the 2 contacts without an email.
Exercise 8
For the maths grades, show on a single row: the number of grades (nb_grades), their average rounded to one decimal place (avg_math) and the gap between the best and the worst grade (spread).
| id | student_id | subject | grade | term |
|---|---|---|---|---|
| 1 | 1 | math | 15 | 1 |
| 2 | 1 | french | 12 | 1 |
| 3 | 1 | math | 17 | 2 |
| 4 | 2 | math | 9 | 1 |
| 5 | 2 | french | 14 | 1 |
| 6 | 2 | math | 11 | 2 |
| 7 | 3 | math | 13 | 1 |
| 8 | 3 | history | 16 | 1 |
| 9 | 4 | math | 18 | 1 |
| 10 | 4 | french | 16 | 1 |
| 11 | 4 | math | 19 | 2 |
| 12 | 5 | french | 8 | 1 |
| 13 | 5 | math | 10 | 1 |
| 14 | 6 | math | 14 | 1 |
| 15 | 6 | history | 11 | 1 |
| … and 3 more rows | ||||
Show the hints
Hint 1 · lead: First filter the maths grades. A calculation can combine two aggregates, just as it combines two columns.
Hint 2 · method: WHERE subject = 'math', then COUNT(*), ROUND(AVG(grade), 1) and MAX(grade) - MIN(grade).
Show the solution
SELECT COUNT(*) AS nb_grades,
ROUND(AVG(grade), 1) AS avg_math,
MAX(grade) - MIN(grade) AS spread
FROM grades
WHERE subject = 'math';Expected result (1 row):
| nb_grades | avg_math | spread |
|---|---|---|
| 9 | 14 | 10 |
WHERE applies before the three aggregates, which therefore only see the maths grades. MAX(grade) - MIN(grade) is a calculation between two aggregates.
Practice with automatic checking
In SpeedQL, you write your query and it is checked straight away, on these tables and then on a hidden dataset.