ποΈ SQL Β· Topic 6 of 20
Aggregate Functions β SQL Questions with Answers
30 practice questions on Aggregate Functions, each with the correct answer and a clear explanation of why it is right. Read through them as revision notes, or take this topic as an interactive quiz to test yourself.
3 easy Β· 15 medium Β· 12 hard Β· Part of SQL: Databases from basics to window functions - 20 topics.
Medium questions (15)
Q3. medium
What is the difference between COUNT(*) and COUNT(column)?
- A COUNT(*) counts every row; COUNT(column) skips rows where that column is NULL
- B They are identical
- C COUNT(column) counts columns
- D COUNT(*) ignores NULLs
Answer: A β COUNT(*) counts every row; COUNT(column) skips rows where that column is NULL
Comparing the two reveals data completeness: COUNT(*) = 100 with COUNT(phone) = 60 means 40 customers have no phone number.
COUNTNULL
Q4. medium
What does COUNT(DISTINCT city) return?
SELECT COUNT(DISTINCT customer_id) AS unique_buyers FROM orders;
- A The number of unique non-NULL cities
- B The total row count
- C A list of cities
- D An error
Answer: A β The number of unique non-NULL cities
DISTINCT inside an aggregate deduplicates before counting - the standard way to answer 'how many different X do we have?'.
COUNT DISTINCT
Q7. medium
How does AVG treat NULL values?
- A It ignores them - dividing only by the count of non-NULL values
- B It treats them as 0
- C It returns NULL if any value is NULL
- D It errors
Answer: A β It ignores them - dividing only by the count of non-NULL values
For 10, 20, NULL the average is 15, not 10. If missing values should count as zero, write AVG(COALESCE(score, 0)).
- This silently changes report numbers - know which behaviour you want.
AVGNULL
Q8. medium
What do MIN() and MAX() do?
- A Return the smallest and largest value - they work on numbers, dates and text
- B Only work on numbers
- C Return the first and last row
- D Count rows
Answer: A β Return the smallest and largest value - they work on numbers, dates and text
MAX(order_date) gives the most recent order; MIN(name) gives the alphabetically first name. Both ignore NULLs.
MINMAX
Q10. medium
Can you use an aggregate in the WHERE clause?
WHERE COUNT(*) > 5
- A No - aggregates are computed after WHERE; use HAVING instead
- B Yes, always
- C Only with COUNT
- D Only in subqueries
Answer: A β No - aggregates are computed after WHERE; use HAVING instead
Execution order is FROM, WHERE, GROUP BY, HAVING. WHERE filters raw rows before any aggregation exists; HAVING filters the aggregated groups.
HAVINGExecution order
Q11. medium
What does this return?
SELECT AVG(price) FROM products;
- A One row with the average price across all products
- B One row per product
- C The sum of prices
- D An error without GROUP BY
Answer: A β One row with the average price across all products
An aggregate without GROUP BY treats the entire result set as one group, always returning exactly one row.
AVGAggregates
Q13. medium
Which query counts orders over 1000?
- A SELECT COUNT(*) FROM orders WHERE total > 1000;
- B SELECT COUNT(total > 1000) FROM orders;
- C SELECT COUNT(*) HAVING total > 1000;
- D SELECT COUNT(WHERE total > 1000);
Answer: A β SELECT COUNT(*) FROM orders WHERE total > 1000;
Filter the rows with WHERE, then count what remains. This is more efficient than aggregating everything and filtering afterwards.
COUNTWHERE
Q15. medium
What does SUM(quantity * price) compute?
- A The total value - each row's product is calculated first, then summed
- B The sum of quantities times the sum of prices
- C An error
- D The average value
Answer: A β The total value - each row's product is calculated first, then summed
Expressions inside an aggregate are evaluated per row. Note SUM(a) * SUM(b) would give a completely different (and usually wrong) answer.
SUMExpressions
Q17. medium
What does COUNT(1) do compared with COUNT(*)?
- A Exactly the same thing - modern optimisers treat them identically
- B COUNT(1) is much faster
- C COUNT(1) counts only the first column
- D COUNT(1) returns 1
Answer: A β Exactly the same thing - modern optimisers treat them identically
An old performance myth. Both count rows; use COUNT(*) as it states the intent clearly.
COUNTMyths
Q19. medium
How do you find the most recent date in a table?
- A SELECT MAX(created_at) FROM t;
- B SELECT LAST(created_at) FROM t;
- C SELECT created_at ORDER BY DESC;
- D SELECT NEWEST(created_at);
Answer: A β SELECT MAX(created_at) FROM t;
MAX works on dates because they have a natural order. To get the whole row of that date, use ORDER BY created_at DESC LIMIT 1.
MAXDates
Q21. medium
Which aggregate finds the cheapest product's PRICE?
- A MIN(price)
- B FIRST(price)
- C LOW(price)
- D BOTTOM(price)
Answer: A β MIN(price)
MIN returns the value only. For the product's name too, either group appropriately or use ORDER BY price ASC LIMIT 1.
MIN
Q24. medium
What does this return if orders is empty?
SELECT COUNT(*) FROM orders;
- A 0
- B NULL
- C No rows
- D An error
Answer: A β 0
COUNT always returns a number, so an empty table gives 0. Contrast with SUM, MIN, MAX and AVG, which return NULL when there is nothing to aggregate.
COUNTEmpty tables
Q26. medium
Which is faster for checking whether any matching row exists?
-- prefer:
IF EXISTS (SELECT 1 FROM orders WHERE customer_id = 5)
- A EXISTS - it stops at the first match, while COUNT(*) scans everything
- B COUNT(*) > 0
- C They are identical
- D SUM(1)
Answer: A β EXISTS - it stops at the first match, while COUNT(*) scans everything
Counting all matches just to compare with zero is wasted work on large tables. EXISTS short-circuits.
EXISTSPerformance
Q28. medium
What does this compute?
SELECT MAX(marks) - MIN(marks) FROM students;
- A The RANGE - the spread between the highest and lowest score
- B The average
- C The count
- D An error
Answer: A β The RANGE - the spread between the highest and lowest score
Aggregates can be combined in expressions, since each returns a single value. Useful for quick data-quality checks.
MINMAXExpressions
Q30. medium
Which query answers 'what is our highest single order value'?
- A SELECT MAX(total) FROM orders;
- B SELECT SUM(total) FROM orders;
- C SELECT COUNT(total) FROM orders;
- D SELECT total FROM orders;
Answer: A β SELECT MAX(total) FROM orders;
MAX gives the largest single value; SUM would give total revenue and COUNT the number of orders. Choosing the right aggregate is choosing the right question.
MAXBusiness questions
Hard questions (12)
Q6. hard
What does SUM(amount) return when the table has NO matching rows?
SELECT COALESCE(SUM(amount), 0) AS total FROM orders WHERE 1=0;
- A NULL - not 0
- B 0
- C An empty string
- D An error
Answer: A β NULL - not 0
A frequent surprise in reports. Wrap it for a numeric default: COALESCE(SUM(amount), 0). Note COUNT does return 0 in the same situation.
SUMNULLCOALESCE
Q9. hard
Why does this fail?
SELECT name, MAX(marks) FROM students;
SELECT name, marks FROM students ORDER BY marks DESC LIMIT 1;
- A name is not aggregated and not grouped - the database cannot know WHICH name to show
- B MAX only works with GROUP BY
- C name must be numeric
- D It always works
Answer: A β name is not aggregated and not grouped - the database cannot know WHICH name to show
MAX collapses many rows to one value, but name has many possible values. Either group by name, or use ORDER BY marks DESC LIMIT 1 to get the whole top row. (MySQL may allow this and return an arbitrary name - a well-known trap.)
GROUP BYAggregates
Q12. hard
How do you round an average to 2 decimal places?
SELECT ROUND(AVG(price), 2) AS avg_price FROM products;
- A ROUND(AVG(price), 2)
- B AVG(ROUND(price, 2))
- C AVG(price).round(2)
- D ROUND(price, 2) AS AVG
Answer: A β ROUND(AVG(price), 2)
Aggregate first, then round the single result. Option B rounds each row before averaging, which can give a slightly different figure.
ROUNDAVG
Q14. hard
What is conditional aggregation?
SELECT
COUNT(*) AS total_orders,
SUM(CASE WHEN status='paid' THEN total ELSE 0 END) AS paid_value,
AVG(CASE WHEN status='paid' THEN total END) AS avg_paid
FROM orders;
- A Using CASE inside an aggregate to compute several different metrics in one query
- B Aggregating with an IF statement outside SQL
- C Filtering with HAVING
- D Using two queries
Answer: A β Using CASE inside an aggregate to compute several different metrics in one query
It produces cross-tab style summaries in a single scan of the table - far faster than running several separate queries.
CASEConditional aggregation
Q16. hard
Can aggregates be nested, like MAX(COUNT(*))?
SELECT MAX(cnt) FROM (
SELECT city, COUNT(*) AS cnt FROM customers GROUP BY city
) AS t;
- A Not directly - you need a subquery or CTE to aggregate an aggregate
- B Yes, always
- C Only with GROUP BY
- D Only MAX can nest
Answer: A β Not directly - you need a subquery or CTE to aggregate an aggregate
Compute the counts in an inner query, then aggregate that result. (Some databases allow MAX(COUNT(*)) with GROUP BY, but the subquery form is clearer and portable.)
SubqueriesNested aggregates
Q18. hard
What does this compute?
SELECT SUM(price) / COUNT(*) FROM products;
- A The average price - but it counts rows with NULL prices, unlike AVG(price)
- B Exactly the same as AVG(price)
- C The total price
- D An error
Answer: A β The average price - but it counts rows with NULL prices, unlike AVG(price)
SUM skips NULLs while COUNT(*) counts every row, so with missing prices the result is LOWER than AVG(price). A subtle reporting discrepancy worth knowing.
AVGNULLGotchas
Q20. hard
What is the difference between COUNT(DISTINCT x) and COUNT(x)?
- A DISTINCT counts unique values; without it, every non-NULL occurrence is counted
- B They are identical
- C DISTINCT includes NULLs
- D COUNT(x) counts uniques
Answer: A β DISTINCT counts unique values; without it, every non-NULL occurrence is counted
In an orders table, COUNT(customer_id) = number of orders, while COUNT(DISTINCT customer_id) = number of customers who ordered. Two very different metrics.
COUNT DISTINCT
Q22. hard
Do aggregates work on text columns?
- A COUNT, MIN and MAX do (alphabetical order); SUM and AVG do not
- B None work on text
- C All work on text
- D Only COUNT works
Answer: A β COUNT, MIN and MAX do (alphabetical order); SUM and AVG do not
MAX(name) returns the alphabetically last name. Attempting SUM on text either errors or silently coerces to 0 depending on the database.
MINMAXData types
Q23. hard
What does STRING_AGG / GROUP_CONCAT do?
SELECT product_id, STRING_AGG(tag, ', ') AS tags
FROM product_tags GROUP BY product_id;
- A Combines values from many rows into ONE delimited string
- B Counts strings
- C Splits a string
- D Sorts strings
Answer: A β Combines values from many rows into ONE delimited string
STRING_AGG(name, ', ') in PostgreSQL/SQL Server, GROUP_CONCAT(name) in MySQL, LISTAGG in Oracle - perfect for listing all tags of a product on one line.
STRING_AGGGROUP_CONCAT
Q25. hard
How do you compute a percentage of the total in one query?
- A Divide by a subquery: amount * 100.0 / (SELECT SUM(amount) FROM t)
- B amount / SUM(amount)
- C PERCENT(amount)
- D It requires two queries
Answer: A β Divide by a subquery: amount * 100.0 / (SELECT SUM(amount) FROM t)
The subquery computes the grand total once. A window function does this even more elegantly: amount * 100.0 / SUM(amount) OVER ().
SubqueriesWindow functions
Q27. hard
What is the median, and why is it awkward in standard SQL?
- A The middle value - there is no simple MEDIAN() in most databases, so it needs window functions or PERCENTILE_CONT
- B The same as AVG
- C A built-in aggregate everywhere
- D Impossible to compute
Answer: A β The middle value - there is no simple MEDIAN() in most databases, so it needs window functions or PERCENTILE_CONT
PostgreSQL and Oracle offer PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x). Elsewhere you rank rows and pick the middle one. Medians resist outliers better than averages.
PercentilesStatistics
Q29. hard
Why can aggregates give wrong totals after a JOIN?
- A A one-to-many join duplicates parent rows, so their values get summed multiple times
- B Aggregates never work with joins
- C Joins remove NULLs
- D It cannot happen
Answer: A β A one-to-many join duplicates parent rows, so their values get summed multiple times
Joining orders to order_items repeats each order once per item, inflating SUM(order.total). Aggregate in a subquery first, or sum the item-level values instead.
- A classic, hard-to-spot reporting bug.
JoinsFan-outAggregates