00:00

MySQL Aggregate Function

Last updated: 4 min read

An aggregate function takes many rows and returns one value: a count, a total, an average, the smallest or largest value. They power every summary report, from "how many orders today?" to "average score per class". This page introduces all of them; the following tutorials go deeper into each.

1. What Aggregates Do

Aggregate functions turn many values into oneA column of five exam scores 72, 85, NULL, 91 and 64 flows into five aggregate functions: COUNT gives 4, SUM 312, AVG 78, MIN 64 and MAX 91. The NULL is ignored by all of them.scorescore7285NULL9164COUNT(score)4SUM(score)312AVG(score)78.0000MIN(score)64MAX(score)91NULL is skippedby every aggregateexcept COUNT(*)
Figure 1: an aggregate function reads a whole set of rows and returns a single value.

Table: results (9 rows)

idstudentclasssubjectscore
1Aditi10AMaths72
2Aditi10AScience85
3Bhanu10AMathsNULL
4Bhanu10AScience91
5Charu10BMaths64
6Charu10BScience58
7Dhruv10BMaths97
8Dhruv10BScience88
9Elina10BMaths81

2. The Aggregate Functions

FunctionReturnsTutorial
COUNT(*) / COUNT(col)Number of rows / non-NULL valuescount()
SUM(col)Total of the valuessum()
AVG(col)Average (mean)avg()
MIN(col) / MAX(col)Smallest / largest valuemin(), max()
GROUP_CONCAT(col)Values joined into one string
JSON_ARRAYAGG(col), JSON_OBJECTAGG(k, v)Values collected into JSON
STDDEV_POP, STDDEV_SAMP, VAR_POP, VAR_SAMPSpread of the values
BIT_AND, BIT_OR, BIT_XORBitwise combination
MySQL has no FIRST() or LAST() aggregate; see first() and last() for the right techniques.

3. Aggregating a Whole Table

query.sql
SELECT COUNT(*)      AS rows_total,
       COUNT(score)  AS scores_recorded,
       SUM(score)    AS total,
       ROUND(AVG(score), 2) AS average,
       MIN(score)    AS lowest,
       MAX(score)    AS highest
FROM results;

Result: 1 row

rows_totalscores_recordedtotalaveragelowesthighest
9863679.505897

4. How Aggregates Treat NULL

  • COUNT(*) counts rows, including rows with NULLs.
  • All other aggregates ignore NULL values (Bhanu's missing Maths score above).
  • On an empty set, COUNT returns 0 and the others return NULL.
query.sql
SELECT COUNT(*) AS n, SUM(score) AS total, AVG(score) AS avg_score, MAX(score) AS top
FROM results WHERE subject = 'History';

Result: 1 row

ntotalavg_scoretop
0NULLNULLNULL
query.sql
SELECT COALESCE(SUM(score), 0) AS total_or_zero FROM results WHERE subject = 'History';

Result: 1 row

total_or_zero
0

5. Aggregates per Group

query.sql
SELECT class, subject, COUNT(score) AS students, ROUND(AVG(score), 1) AS avg_score, MAX(score) AS best
FROM results
GROUP BY class, subject
ORDER BY class, subject;

Result: 4 rows

classsubjectstudentsavg_scorebest
10AMaths172.072
10AScience288.091
10BMaths380.797
10BScience273.088

See GROUP BY and HAVING.

6. DISTINCT Inside an Aggregate

query.sql
SELECT COUNT(DISTINCT student) AS students, COUNT(DISTINCT class) AS classes, SUM(DISTINCT score) AS sum_of_distinct_scores FROM results;

Result: 1 row

studentsclassessum_of_distinct_scores
52636

7. GROUP_CONCAT and JSON Aggregates

query.sql
SELECT class,
       GROUP_CONCAT(DISTINCT student ORDER BY student SEPARATOR ', ') AS students,
       JSON_ARRAYAGG(score) AS all_scores
FROM results
GROUP BY class;

Result: 2 rows

classstudentsall_scores
10AAditi, Bhanu[72, 85, null, 91]
10BCharu, Dhruv, Elina[64, 58, 97, 88, 81]
GROUP_CONCAT output is cut at group_concat_max_len (1024 bytes by default). Raise it with SET SESSION group_concat_max_len = 100000; for long lists.

8. Statistical Aggregates

query.sql
SELECT subject,
       ROUND(AVG(score), 2)        AS mean,
       ROUND(STDDEV_POP(score), 2) AS std_dev,
       ROUND(VAR_POP(score), 2)    AS variance
FROM results
GROUP BY subject;

Result: 2 rows

subjectmeanstd_devvariance
Maths78.5012.26150.25
Science80.5013.16173.25

9. Aggregates as Window Functions

Add OVER (…) and an aggregate no longer collapses rows: each row keeps its details plus the group's value. Compare each score with its class average:

window.sql
SELECT student, class, subject, score,
       ROUND(AVG(score) OVER (PARTITION BY class, subject), 1) AS class_avg,
       score - ROUND(AVG(score) OVER (PARTITION BY class, subject), 1) AS diff
FROM results
WHERE subject = 'Maths'
ORDER BY class, score DESC;

Result: 5 rows

studentclasssubjectscoreclass_avgdiff
Aditi10AMaths7272.00.0
Bhanu10AMathsNULL72.0NULL
Dhruv10BMaths9780.716.3
Elina10BMaths8180.70.3
Charu10BMaths6480.7-16.7

10. Rules to Remember

  • You can't use an aggregate in WHERE; use HAVING for conditions on aggregates.
  • Every non-aggregated column in SELECT must be in GROUP BY.
  • Aggregates can't be nested directly (MAX(AVG(x))); use a sub-query or CTE.
  • Integer SUM returns DECIMAL; AVG of integers returns DECIMAL with 4 extra decimal places.
query.sql
SELECT MAX(avg_score) AS best_class_average
FROM (SELECT class, AVG(score) AS avg_score FROM results GROUP BY class) t;

Result: 1 row

best_class_average
82.6667

11. Summary

  • Aggregates (COUNT, SUM, AVG, MIN, MAX, GROUP_CONCAT, JSON_ARRAYAGG, STDDEV…) turn many rows into one value.
  • They ignore NULLs, except COUNT(*).
  • Combine with GROUP BY for per-group results and HAVING to filter groups.
  • Use DISTINCT inside to aggregate unique values; add OVER() to keep row details.