Aggregate Functions: AVG, MAX, MIN, and COUNT Together
Summarize student grades per course using AVG, MAX, MIN, and COUNT alongside a join for readable course names.
Overview
Aggregate functions turn a column of many values into a single summary
value. AVG, MAX, MIN, and COUNT are the four you'll use most often,
and they're frequently combined in a single query — for example, computing
the average, highest, lowest, and total number of scores recorded for each
course. Joining in a descriptive table (here, courses) lets you show a
readable name instead of a bare course_id.
This pattern shows up anywhere you need a quick statistical snapshot: grade distributions, sales performance per product, response times per endpoint, and so on.
The Query
Loading database engine...
Step-by-Step Breakdown
FROM grades g JOIN courses c ON g.course_id = c.id— attach each grade row to its course so we can display a name instead of just an ID.GROUP BY c.id, c.name— bucket grade rows by course. Bothc.idandc.nameare included in theGROUP BYeven thoughc.idalone would uniquely determine the group, because most databases require every selected non-aggregated column to appear in theGROUP BYlist.AVG(g.score)— the mean score recorded for that course, across all students and terms present in the data.MAX(g.score)/MIN(g.score)— the highest and lowest individual scores recorded for that course.COUNT(*)— how many grade records exist for that course (not the number of distinct students — a student could appear more than once across terms).ORDER BY avg_score DESC— courses with the highest average score first.
Variations
Restrict the same aggregation to a single term:
Loading database engine...
Filtering with WHERE g.term = 'Fall 2022' happens before grouping, so the
averages reflect only that term's grade records rather than every term ever
recorded for a course.
AVG ignores NULLs, COUNT(*) doesn't
If some score values were NULL (say, an ungraded assignment), AVG,
MAX, and MIN would silently skip them in their calculation, but
COUNT(*) would still count those rows. Use COUNT(g.score) instead of
COUNT(*) if you specifically want "the number of non-null scores."
Common Mistakes
- Mixing up
COUNT(*)andCOUNT(column). They behave differently the momentNULLvalues are involved —COUNT(*)counts rows,COUNT(column)counts non-null values in that column. - Averaging an already-averaged value.
AVGof a column that's itself a per-group average (from a subquery, say) doesn't equal the true overall average unless every group has the same number of underlying rows. - Forgetting
GROUP BYneeds every non-aggregated selected column. Addingc.nameto theSELECTlist without adding it toGROUP BY(when only grouping byc.id) will error in strict databases. - Comparing MAX/MIN across groups of very different sizes. A course with
only two grades recorded can have a misleadingly extreme MAX or MIN
compared to a course with fifty grades — pair these with
COUNT(*)so the sample size is visible.