你是否曾想过,数据库如何能够瞬间告诉你全年的总销售额、客户的平均年龄,或者上个月下了多少订单?答案就在 SQL 聚合函数中------这些强大的工具可以将多行数据转换为有意义的汇总信息。

聚合函数将多行数据汇总为单个指标
在本指南中,我们将探讨五个核心聚合函数:COUNT、SUM、AVG、MIN 和 MAX。读完本文后,你将准确了解何时以及如何使用每个函数,包括一些连经验丰富的开发人员都会犯的常见错误。
什么是聚合函数?
聚合函数(Aggregate Functions)对多行数据执行计算,并返回单个结果。与处理单个值的常规函数不同,聚合函数会将数据"折叠"成汇总统计信息。
以下是快速概览:
| 函数 | 用途 | 返回值类型 |
|---|---|---|
COUNT() |
计数行或非 NULL 值 | 整数 |
SUM() |
求和数值 | 数字 |
AVG() |
计算平均值 | 数字 |
MIN() |
查找最小值 | 与输入相同 |
MAX() |
查找最大值 | 与输入相同 |
核心特点:
- 多行输入,单个输出:处理多行数据,返回一个汇总值
- 自动忽略 NULL:大多数聚合函数会自动忽略 NULL 值
- 与 GROUP BY 结合:可以按组进行汇总
- 适用范围广:MIN 和 MAX 可用于数字、字符串、日期等多种数据类型
COUNT:计数行和值
COUNT 可能是最常用的聚合函数。但你知道吗,它有三种不同的使用方式?
COUNT(*) vs COUNT(column) vs COUNT(DISTINCT column)
sql
-- 计数所有行(包括 NULL)
SELECT COUNT(*) FROM orders;
-- 计数列中的非 NULL 值
SELECT COUNT(shipping_date) FROM orders;
-- 计数唯一的非 NULL 值
SELECT COUNT(DISTINCT customer_id) FROM orders;
关键区别:
COUNT(*):计数所有行,包括包含 NULL 的行COUNT(column):只计数该列中非 NULL 的行COUNT(DISTINCT column):计数该列中唯一的非 NULL 值
交互式示例:COUNT 的三种用法
示例数据(employees 表):
| id | name | department | salary | bonus |
|---|---|---|---|---|
| 1 | Alice | Engineering | 90000 | 5000 |
| 2 | Bob | Engineering | 85000 | NULL |
| 3 | Carol | Sales | 70000 | 3000 |
| 4 | Dave | Sales | 65000 | NULL |
| 5 | Eve | Marketing | 75000 | 4000 |
查询:
sql
-- 尝试不同的 COUNT 变体
SELECT
COUNT(*) AS total_rows,
COUNT(bonus) AS with_bonus,
COUNT(DISTINCT department) AS unique_departments
FROM employees;
查询结果:
| total_rows | with_bonus | unique_departments |
|---|---|---|
| 5 | 3 | 3 |
结果解读:
total_rows = 5:表中共有 5 行数据with_bonus = 3:只有 3 个员工有奖金(Alice、Carol、Eve)unique_departments = 3:有 3 个不同的部门(Engineering、Sales、Marketing)
注意 :COUNT(bonus) 返回 3(只计数非 NULL 值),而 COUNT(*) 返回 5(所有行)。
SUM:求和数值
SUM 将数值列中的所有值相加。它会自动忽略 NULL 值。
sql
SELECT SUM(amount) AS total_sales
FROM orders
WHERE order_date >= '2025-01-01';
常见陷阱:NULL 值处理
对所有值都是 NULL 的列求和会返回 NULL,而不是 0。使用 COALESCE 来处理这种情况:
sql
SELECT COALESCE(SUM(amount), 0) AS total_sales
FROM orders
WHERE order_date >= '2099-01-01'; -- 没有匹配的行
交互式示例:SUM 和 NULL 处理
示例数据(orders 表):
| order_id | amount | discount | bonus |
|---|---|---|---|
| 1 | 1000 | 50 | 100 |
| 2 | 1500 | NULL | NULL |
| 3 | 2000 | 100 | 200 |
| 4 | 1200 | NULL | NULL |
| 5 | 1800 | 75 | 150 |
查询:
sql
-- 计算总额并查看 NULL 处理
SELECT
SUM(amount) AS total_sales,
SUM(discount) AS total_discount,
COALESCE(SUM(bonus), 0) AS total_bonus
FROM orders;
查询结果:
| total_sales | total_discount | total_bonus |
|---|---|---|
| 7500 | 225 | 450 |
结果解读:
total_sales = 7500:所有订单金额之和(1000 + 1500 + 2000 + 1200 + 1800)total_discount = 225:所有折扣之和(50 + 100 + 75),NULL 值被忽略total_bonus = 450:所有奖金之和(100 + 200 + 150),NULL 值被忽略
关键点:
- SUM 自动忽略 NULL 值
- 如果所有值都是 NULL,SUM 返回 NULL
- 使用 COALESCE 将 NULL 转换为 0
AVG:计算平均值
AVG 计算数值列的算术平均值。与 SUM 一样,它会忽略 NULL 值------这有时会导致意外的结果。
sql
SELECT AVG(salary) AS avg_salary
FROM employees;
重要提示:AVG 只考虑非 NULL 值
AVG 在计算时只考虑非 NULL 值。如果你有 10 个员工,但只有 8 个有薪资数据,平均值是基于这 8 个计算的。
交互式示例:AVG 与手动计算对比
示例数据(reviews 表):
| review_id | product_id | rating |
|---|---|---|
| 1 | 101 | 5 |
| 2 | 101 | 4 |
| 3 | 101 | NULL |
| 4 | 101 | 3 |
| 5 | 101 | 5 |
查询:
sql
-- 比较 AVG 与手动计算
SELECT
AVG(rating) AS avg_rating,
SUM(rating) AS sum_rating,
COUNT(rating) AS count_rating,
SUM(rating) * 1.0 / COUNT(rating) AS manual_avg
FROM reviews;
查询结果:
| avg_rating | sum_rating | count_rating | manual_avg |
|---|---|---|---|
| 4.25 | 17 | 4 | 4.25 |
结果解读:
avg_rating = 4.25:AVG 函数自动计算平均值sum_rating = 17:所有评分之和(5 + 4 + 3 + 5),NULL 被忽略count_rating = 4:非 NULL 评分的数量manual_avg = 4.25:手动计算的平均值(17 / 4)
关键点:
- AVG 只考虑非 NULL 值
- 如果有 5 条记录但只有 4 个非 NULL 值,平均值基于这 4 个值
- 手动计算:
SUM(column) / COUNT(column)
MIN 和 MAX:查找极值
MIN 和 MAX 适用于任何可比较的数据类型------数字、字符串、日期等。
sql
SELECT
MIN(order_date) AS first_order,
MAX(order_date) AS last_order,
MIN(amount) AS smallest_order,
MAX(amount) AS largest_order
FROM orders;
专业提示:字符串的 MIN 和 MAX
对于字符串,MIN 返回"字母顺序最前"的值,MAX 返回"字母顺序最后"的值。
交互式示例:不同数据类型的极值
示例数据(employees 表):
| id | name | hire_date | salary |
|---|---|---|---|
| 1 | Alice | 2020-01-15 | 90000 |
| 2 | Bob | 2019-06-20 | 85000 |
| 3 | Carol | 2021-03-10 | 70000 |
| 4 | Dave | 2018-11-05 | 65000 |
| 5 | Eve | 2022-07-22 | 75000 |
查询:
sql
-- 查找不同数据类型的极值
SELECT
MIN(hire_date) AS earliest_hire,
MAX(hire_date) AS latest_hire,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary,
MIN(name) AS first_name_alpha,
MAX(name) AS last_name_alpha
FROM employees;
查询结果:
| earliest_hire | latest_hire | min_salary | max_salary | first_name_alpha | last_name_alpha |
|---|---|---|---|---|---|
| 2018-11-05 | 2022-07-22 | 65000 | 90000 | Alice | Eve |
结果解读:
earliest_hire = 2018-11-05:最早的雇佣日期(Dave)latest_hire = 2022-07-22:最晚的雇佣日期(Eve)min_salary = 65000:最低薪资(Dave)max_salary = 90000:最高薪资(Alice)first_name_alpha = Alice:字母顺序最前的名字last_name_alpha = Eve:字母顺序最后的名字
关键点:
- MIN 和 MAX 适用于数字、字符串、日期等多种数据类型
- 对于字符串,按字母顺序比较
- 对于日期,按时间顺序比较
结合 GROUP BY 使用聚合函数
聚合函数与 GROUP BY 结合使用时会更加强大。你可以为每个组获得汇总信息,而不是整个表的单个汇总。
sql
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department;
交互式示例:按部门汇总
示例数据(employees 表):
| id | name | department | salary |
|---|---|---|---|
| 1 | Alice | Engineering | 90000 |
| 2 | Bob | Engineering | 85000 |
| 3 | Carol | Sales | 70000 |
| 4 | Dave | Sales | 65000 |
| 5 | Eve | Marketing | 75000 |
| 6 | Frank | Engineering | 95000 |
| 7 | Grace | Sales | 72000 |
查询:
sql
-- 按部门汇总
SELECT
department,
COUNT(*) AS headcount,
SUM(salary) AS total_payroll,
ROUND(AVG(salary), 2) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department;
查询结果:
| department | headcount | total_payroll | avg_salary | min_salary | max_salary |
|---|---|---|---|---|---|
| Engineering | 3 | 270000 | 90000.00 | 85000 | 95000 |
| Sales | 3 | 207000 | 69000.00 | 65000 | 72000 |
| Marketing | 1 | 75000 | 75000.00 | 75000 | 75000 |
结果解读:
- Engineering 部门:3 名员工,总薪资 270,000,平均薪资 90,000
- Sales 部门:3 名员工,总薪资 207,000,平均薪资 69,000
- Marketing 部门:1 名员工,总薪资 75,000,平均薪资 75,000
关键点:
- GROUP BY 将数据分组,每组单独计算聚合函数
- 可以在一个查询中使用多个聚合函数
- 使用 ROUND 函数使平均值更易读
常见错误
1. 混合聚合列和非聚合列
这是一个经典错误:
sql
-- ❌ 错误:name 既没有聚合也没有分组
SELECT name, COUNT(*) FROM employees;
-- ✅ 正确:按 name 分组
SELECT name, COUNT(*) FROM employees GROUP BY name;
错误原因:
- 当使用聚合函数时,SELECT 中的所有非聚合列都必须出现在 GROUP BY 中
- 否则数据库不知道如何处理这些列
2. 忘记 NULL 行为
sql
-- 如果所有值都是 NULL,SUM 返回 NULL(不是 0)
SELECT COALESCE(SUM(bonus), 0) AS total_bonus FROM employees;
最佳实践:
- 始终使用 COALESCE 或 IFNULL 处理可能的 NULL 结果
- 特别是在报表和仪表板中,0 比 NULL 更友好
3. 在整数列上使用 AVG
在某些数据库中,对整数列使用 AVG 会返回整数。转换类型以避免这种情况:
sql
SELECT AVG(CAST(rating AS REAL)) FROM reviews;
-- 或者:AVG(rating * 1.0)
示例:
| 错误方式 | 正确方式 |
|---|---|
AVG(rating) → 4(整数除法) |
AVG(rating * 1.0) → 4.25(浮点除法) |
最佳实践
1. 使用 COUNT(*) 计数行
COUNT(*) 是计数所有行的最清晰方式。
sql
-- ✅ 推荐
SELECT COUNT(*) FROM orders;
-- ❌ 不推荐(除非你确实只想计数非 NULL 值)
SELECT COUNT(order_id) FROM orders;
2. 使用 COALESCE 处理 SUM/AVG
保护查询免受 NULL 结果的影响,特别是当没有行匹配时。
sql
SELECT
COALESCE(SUM(amount), 0) AS total_sales,
COALESCE(AVG(amount), 0) AS avg_sales
FROM orders
WHERE order_date >= '2099-01-01'; -- 没有匹配的行
3. 四舍五入 AVG 结果
ROUND(AVG(salary), 2) 提供更清晰的输出。
sql
-- ❌ 不易读
SELECT AVG(salary) FROM employees;
-- 结果:75432.8571428571
-- ✅ 易读
SELECT ROUND(AVG(salary), 2) FROM employees;
-- 结果:75432.86
4. 结合 HAVING 进行过滤聚合
在分组后过滤使用 HAVING。
sql
-- 查找平均薪资超过 80000 的部门
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 80000;
WHERE vs HAVING:
| 特性 | WHERE | HAVING |
|---|---|---|
| 过滤时机 | 分组前 | 分组后 |
| 可用列 | 原始列 | 聚合结果 |
| 示例 | WHERE salary > 50000 |
HAVING AVG(salary) > 80000 |
实际应用场景
1. 销售报表
sql
-- 按月汇总销售数据
SELECT
DATE_TRUNC('month', order_date) AS month,
COUNT(*) AS order_count,
SUM(amount) AS total_revenue,
ROUND(AVG(amount), 2) AS avg_order_value,
MIN(amount) AS min_order,
MAX(amount) AS max_order
FROM orders
WHERE order_date >= '2025-01-01'
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month;
2. 用户行为分析
sql
-- 分析用户活跃度
SELECT
user_id,
COUNT(*) AS login_count,
MIN(login_date) AS first_login,
MAX(login_date) AS last_login,
COUNT(DISTINCT DATE(login_date)) AS active_days
FROM user_logins
WHERE login_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
HAVING COUNT(*) >= 10;
3. 库存管理
sql
-- 库存统计
SELECT
category,
COUNT(*) AS product_count,
SUM(quantity) AS total_stock,
AVG(price) AS avg_price,
MIN(quantity) AS min_stock,
MAX(quantity) AS max_stock
FROM inventory
GROUP BY category
HAVING SUM(quantity) < 100; -- 库存不足的类别
4. 性能监控
sql
-- API 响应时间分析
SELECT
endpoint,
COUNT(*) AS request_count,
ROUND(AVG(response_time_ms), 2) AS avg_response_time,
MIN(response_time_ms) AS min_response_time,
MAX(response_time_ms) AS max_response_time
FROM api_logs
WHERE log_date >= CURRENT_DATE
GROUP BY endpoint
HAVING AVG(response_time_ms) > 1000; -- 响应时间超过 1 秒的端点
结论
SQL 聚合函数是数据分析的核心工具。记住:
- COUNT 计数行或非 NULL 值
- SUM 求和数字(忽略 NULL)
- AVG 计算平均值(忽略 NULL)
- MIN/MAX 查找极值(适用于任何可比较类型)
关键要点:
- 聚合函数通常会忽略 NULL 值,但这可能导致意外结果
- 始终使用
COALESCE确保非 NULL 结果 - 结合
GROUP BY使用可以进行分组汇总 - 使用
HAVING过滤聚合结果
现在,去尝试这些函数吧!在实际项目中实验不同的查询,以巩固你的理解。
相关文章推荐
- SQL ORDER BY 和 LIMIT:轻松实现排序和分页 - 学习如何使用 ORDER BY 排序查询结果和分页
- SQL Window Frames: ROWS vs RANGE - 学习 ROWS 和 RANGE 窗口帧如何改变结果,避免隐藏的 bug
- 使用 SQL 计算移动平均和滚动窗口 - 使用 SQL 窗口函数平滑噪声数据趋势
本文转载自 www.hisqlboy.com
原文标题:SQL Aggregate Functions: COUNT, SUM, AVG, MIN, MAX Explained
原文链接:https://www.hisqlboy.com/blog/sql-aggregate-functions-explained
原作者:SQL Boy Team
转载日期:2026-02-15