COUNT, SUM, AVG, MIN and MAX
The five aggregate functions that collapse many rows into one value, the three forms of COUNT, and what each one does with NULL.
- SQL Basics
- DDL
- DML
- SELECT
- WHERE
- Functions
- NULL and Logic
- Aggregate Functions
- GROUP BY
- JOIN
- Subqueries
- Set Operations
- CTEs
- Constraints
- Keys
- Relationships
- Database Design
- Normalisation
- Views
- Window Functions
- Advanced SQL
- Procedures and Functions
- Triggers
- Temporary Tables
- Transactions
- Isolation and Locking
- Indexes
- Query Performance
- SQL Security
- SQL Dialects
- Practical SQL
Concept
An aggregate function takes many rows and returns one value. Used without GROUP BY, it collapses the entire result into a single row.
| Function | Returns | Ignores NULL |
|---|---|---|
COUNT(*) | number of rows | No - counts every row |
COUNT(column) | number of non NULL values | Yes |
COUNT(DISTINCT column) | number of distinct non NULL values | Yes |
SUM(column) | total | Yes |
AVG(column) | mean of the present values | Yes |
MIN(column) / MAX(column) | smallest / largest | Yes |
Syntax
SELECT aggregate_function(column_or_expression)
FROM table_name
WHERE condition;Example
SELECT COUNT(*) AS employee_count,
COUNT(dept_id) AS with_department,
COUNT(DISTINCT dept_id) AS distinct_departments,
SUM(salary) AS total_payroll,
ROUND(AVG(salary), 2) AS average_salary,
MIN(salary) AS lowest,
MAX(salary) AS highest,
MAX(salary) - MIN(salary) AS salary_spread
FROM employees;-- Aggregates respect WHERE, which runs first
SELECT COUNT(*) AS active_employees,
SUM(salary) AS active_payroll
FROM employees
WHERE status = 'active';Explanation
On the sample data COUNT(*) returns 8 but COUNT(dept_id) returns 7, because Nikhil has no department. That gap is the fastest way to measure missing data in a column:
SELECT COUNT(*) - COUNT(email) AS missing_emails FROM employees;MIN and MAX are not limited to numbers. On a DATE they give the earliest and latest; on text they follow the collation's sort order:
SELECT MIN(hire_date) AS first_hire,
MAX(hire_date) AS latest_hire,
MIN(last_name) AS alphabetically_first
FROM employees;Important rules
- Every aggregate except
COUNT(*)ignoresNULL. AVG(col)isSUM(col) / COUNT(col), notSUM(col) / COUNT(*). Rows with NULL are not in the denominator.- Aggregates cannot appear in
WHERE. Filtering on an aggregate is whatHAVINGis for. - Aggregating an empty set gives
NULLforSUM,AVG,MINandMAX, but0forCOUNT. - Aggregates cannot be nested directly:
MAX(AVG(salary))is invalid without a subquery or aGROUP BYunderneath it.
Common mistakes
- Reporting
AVG(bonus)as "average bonus per employee" when employees without a bonus are excluded from the divisor. UseAVG(COALESCE(bonus, 0))if they should count as zero. - Writing
WHERE COUNT(*) > 5. It is a syntax error; useHAVING. - Assuming
SUMof an empty result is 0. It isNULL- wrap it:COALESCE(SUM(total), 0). - Using
COUNT(column)when the intent was to count rows, and silently under counting. - Mixing a bare column with an aggregate and no
GROUP BY, which is an error in standard SQL and a silently arbitrary value in loose MySQL modes.
Best practices
- Use
COUNT(*)to count rows andCOUNT(col)only when you specifically mean "values present". - Wrap aggregates that feed a display in
COALESCE(..., 0)so an empty result shows zero, not blank. - Round money aggregates explicitly.
- State the NULL policy in the column alias:
avg_salary_of_paid_staffbeatsavg_salary.
Practice
- Find the total, average, smallest and largest order value in the sample
orderstable. - Count how many customers have placed at least one order, and how many exist in total.
- Explain the difference between
COUNT(*),COUNT(dept_id)andCOUNT(DISTINCT dept_id)on the sample employees.