📌 一句话总结
聚合函数对一组行进行计算并返回一个汇总值 ,是 SQL 做统计分析的基石。常与 GROUP BY 配对使用(今天的铺垫)。
1. 五大聚合函数一览
| 函数 | 作用 | 适用类型 | NULL 处理 |
|---|---|---|---|
COUNT(*) |
统计行数 | 任何 | 所有行都算 |
COUNT(column) |
统计非 NULL 值个数 | 任何 | 剔除 NULL |
SUM(column) |
求和 | 数值 | 忽略 NULL |
AVG(column) |
求平均值 | 数值 | 忽略 NULL |
MIN(column) |
求最小值 | 数值/字符串/日期 | 忽略 NULL |
MAX(column) |
求最大值 | 数值/字符串/日期 | 忽略 NULL |
sql
-- 基础示例
SELECT
COUNT(*) AS total_employees,
COUNT(department_id) AS has_dept, -- 比 COUNT(*) 少(有空部门)
COUNT(DISTINCT department_id) AS unique_depts, -- 去重统计
AVG(salary) AS avg_salary,
SUM(salary) AS total_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary
FROM employees;
2. NULL 的处理陷阱
sql
-- 建测试表
CREATE TABLE scores (name TEXT, score INT);
INSERT INTO scores VALUES ('张三', 85), ('李四', NULL), ('王五', 92), ('赵六', NULL);
-- 不同 COUNT 的区别
SELECT
COUNT(*) AS total_rows, -- 4(所有行)
COUNT(score) AS has_score, -- 2(忽略 NULL)
AVG(score) AS avg_score, -- 88.5((85+92)/2,不是 /4)
SUM(score)/4 AS wrong_avg, -- 44.25(千万别这么写)
SUM(score)/COUNT(score) AS correct_avg -- 88.5
FROM scores;
⚠️ AVG + NULL 的坑
AVG 内部是 SUM / COUNT(column),不是 SUM / COUNT(*)。如果行的 NULL 太多,算出来的平均值可能比预期高(因为分母变小了)。
sql
-- 处理方式 1:把 NULL 视为 0
SELECT AVG(COALESCE(score, 0)) AS avg_including_zero FROM scores;
-- (85+0+92+0)/4 = 44.25
-- 处理方式 2:明确业务语义
-- "缺考"和"考了0分"是两回事,业务上通常是 方式1=实际平均 方式2=含缺考
3. DISTINCT 聚合
sql
-- 去重统计
SELECT
COUNT(DISTINCT city) AS unique_cities,
SUM(DISTINCT salary) AS sum_unique_salaries -- 不重复薪资之和(用得少)
FROM employees;
COUNT(DISTINCT col) 非常常用,「有多少种不同的值」。 SUM(DISTINCT col) 很罕见,只有在「每种值只算一次」的特殊业务才有用。
4. 聚合 + 条件过滤
三步走:WHERE 先过滤 → 再聚合。
sql
-- 查询技术部的薪资统计
SELECT
COUNT(*) AS tech_count,
AVG(salary) AS tech_avg_salary
FROM employees
WHERE department = '技术部';
-- 多条件组合
SELECT
COUNT(*) AS count_high_salary,
AVG(salary) AS avg_high_salary
FROM employees
WHERE salary > 15000
AND hire_date >= '2023-01-01';
WHERE vs HAVING(预告)
WHERE:在聚合前过滤行HAVING:在聚合后过滤组
sql
-- WHERE 过滤掉薪资为 NULL 的行再聚合
SELECT AVG(salary) FROM employees WHERE salary IS NOT NULL;
-- 上面等价于 SELECT AVG(salary) FROM employees;(AVG 本身就忽略 NULL)
5. 实战案例
案例 1:报表统计
sql
-- 公司整体报表
SELECT
COUNT(*) AS 员工总数,
COUNT(CASE WHEN gender = '女' THEN 1 END) AS 女性人数,
ROUND(AVG(salary), 0) AS 平均薪资,
MAX(salary) AS 最高薪资,
MIN(salary) AS 最低薪资,
MAX(salary) - MIN(salary) AS 薪资差距
FROM employees;
案例 2:日期聚合
sql
-- 最早和最晚入职日期
SELECT
MIN(hire_date) AS 最早入职,
MAX(hire_date) AS 最晚入职,
DATEDIFF(MAX(hire_date), MIN(hire_date)) AS 公司历史天数 -- MySQL
FROM employees;
案例 3:数据质量检测
sql
-- 检查各字段的完整性
SELECT
COUNT(*) AS total,
COUNT(email) AS has_email,
COUNT(phone) AS has_phone,
COUNT(department_id) AS has_dept,
COUNT(salary) AS has_salary
FROM employees;
-- has_email < total 说明有人没填邮箱
6. 聚合函数与非聚合列
sql
-- ❌ 错误写法
SELECT name, AVG(salary) FROM employees;
-- name 是非聚合列,但有 100 行,AVG(salary) 只有 1 个值
-- 数据库不知道要显示哪个 name
-- MySQL 默认会取第一个 name(不报错但语义错误!)
-- PostgreSQL / SQLite 直接报错
-- ✅ 正确做法
SELECT AVG(salary) FROM employees; -- 只查聚合
SELECT department_id, AVG(salary) -- 加 GROUP BY
FROM employees
GROUP BY department_id;
⚠️ 规则: SELECT 中出现非聚合列,必须出现在
GROUP BY中(MySQL 宽松模式除外,但不要依赖)。
📝 要点总结
| 函数 | 关键点 |
|---|---|
COUNT(*) |
统计行数,含 NULL |
COUNT(col) |
统计非 NULL 行数 |
AVG(col) |
忽略 NULL 后计算,分母不含 NULL |
MIN/MAX |
字符串按字典序,日期按时间序 |
| NULL | 所有聚合函数(除 COUNT(*))都忽略 NULL |
| DISTINCT | COUNT(DISTINCT col) 去重计数 |
| 与非聚合列混用 | 必须配合 GROUP BY,否则语义错误 |