前言
三位数据分析师走进会议室。他们被要求计算同一个数据库中的员工平均薪资。
分析师 A 报告:67,500 元
分析师 B 报告:54,000 元
分析师 C 报告:72,000 元
没有人打错字。没有查询报错。三个查询都成功执行了——而且从技术上讲,这三个答案都是「正确」的。
这是一个令人不安的真相:如果你在使用 SQL 的 AVG() 函数时,不了解它如何处理 NULL 值,你的数据分析很可能是错的。更糟的是,你永远不会收到错误消息告诉你这件事。
读完本文,你将学会:
-
✅ 理解 AVG() 函数如何处理 NULL 值的底层逻辑
-
✅ 识别哪些场景下 NULL 排除会导致错误的业务洞察
-
✅ 掌握三种实用的 NULL 处理方法(COALESCE、WHERE、CASE)
-
✅ 使用决策框架快速选择正确的平均值计算方式
-
✅ 避免 90% 初学者都会犯的 AVG() 使用错误
博主提醒:这不是一个 SQL 语法错误,而是一个设计特性。但如果你不理解它,同样的数据可能会给你三个完全不同的平均值,而你永远不知道哪个才是你真正需要的。
一、问题复现:为什么三个人算出了三个不同的平均值?
1.1 真实场景还原
假设我们有一个员工表,包含 5 名员工的薪资信息:
CREATE TABLE employees (
employee_id INT,
name VARCHAR(100),
salary DECIMAL(10,2)
);
INSERT INTO employees VALUES
(1, '张三', 70000),
(2, '李四', 65000),
(3, '王五', NULL), -- 新入职员工,薪资尚未设定
(4, '赵六', NULL), -- 合同工,无底薪
(5, '孙七', 75000);
现在,三位分析师分别执行了不同的查询:
分析师 A 的查询:
SELECT AVG(salary) AS avg_salary
FROM employees;
-- 结果:70,000
-- 计算方式:(70000 + 65000 + 75000) / 3 = 70,000
分析师 B 的查询:
SELECT AVG(COALESCE(salary, 0)) AS avg_salary
FROM employees;
-- 结果:42,000
-- 计算方式:(70000 + 65000 + 0 + 0 + 75000) / 5 = 42,000
分析师 C 的查询:
SELECT AVG(salary) AS avg_salary
FROM employees
WHERE salary IS NOT NULL;
-- 结果:70,000(与查询 A 相同,但意图更明确)
1.2 真相揭秘:AVG() 的隐藏行为
大多数初学者认为 AVG() 的工作方式是这样的:
-
取得列中的所有数字
-
加总起来
-
除以行数
-
返回平均值
但实际上,AVG() 的真实行为是:
| 步骤 | 初学者以为的 | AVG() 实际的行为 |
|---|---|---|
| 1. 数据选择 | 取所有行的值 | 只取非 NULL 的值 |
| 2. 求和 | 加总所有值 | 只加总非 NULL 的值 |
| 3. 计数 | 除以总行数 | 除以非 NULL 值的数量 |
| 4. 返回 | 返回结果 | 返回结果(完全忽略 NULL) |
💡 关键点:
AVG()会自动排除 NULL 值,这不是 bug,而是 SQL 标准的设计特性。NULL 在 SQL 中代表「未知」或「不适用」,不等于零。
二、NULL 排除的设计原理:什么时候这是正确的?
2.1 为什么 SQL 要这样设计?
SQL 将 NULL 视为「未知」或「不适用」,而不是「零」。这是一个深思熟虑的设计决策。
2.2 三个合理的场景
场景 1:产品评分系统
-- 电商平台的产品评分表
-- 部分产品有评分,部分产品还未被评分
SELECT AVG(rating) AS avg_rating
FROM products;
| 解读方式 | 结果 | 是否合理 |
|---|---|---|
| ✅ 已被评分产品的平均分 | 4.5 星 | 合理:反映真实用户评价 |
| ❌ 将未评分当作 0 星 | 2.1 星 | 不合理:未评分 ≠ 差评 |
场景 2:问卷调查的满意度
-- 客户满意度调查(1-10 分)
-- 许多客户跳过了这个问题
SELECT AVG(satisfaction_score) AS avg_satisfaction
FROM surveys;
正确理解:回答问题的客户的平均满意度
错误理解:将未回答视为 0 分不满意
场景 3:员工绩效奖金
-- 大多数员工没有绩效奖金
SELECT AVG(bonus_amount) AS avg_bonus
FROM employees;
正确理解:获得奖金的员工的平均奖金额
错误理解:所有员工的平均奖金(应该包括零奖金员工)
🎯 设计原则:当 NULL 真正代表「不适用」或「未知」时,排除它们是正确的。问题在于,你的 NULL 到底代表什么?
三、危险场景:NULL 排除导致的分析错误
3.1 错误案例 1:销售团队业绩统计
业务背景:
-
销售团队按提成工作
-
大部分销售每月都有业绩
-
少数销售这个月业绩为零,在系统中记录为 NULL
-- ❌ 错误查询:只统计了有业绩的销售
SELECT AVG(commission) AS avg_commission
FROM sales_reps
WHERE month = '2025-10';
-- 结果:4,500 元
-- 问题:排除了无业绩的销售,夸大了平均业绩
向管理层报告:「本月平均提成 4,500 元」
实际情况:包含零业绩销售后,真实平均只有 3,200 元
-- ✅ 正确查询:将 NULL 视为零提成
SELECT AVG(COALESCE(commission, 0)) AS avg_commission
FROM sales_reps
WHERE month = '2025-10';
-- 结果:3,200 元
-- 说明:真实反映了团队整体表现
3.2 错误案例 2:网站用户行为分析
业务背景:
-
分析用户在网站停留时长
-
NULL 代表用户立即跳出(0 秒停留)
-- ❌ 错误查询:只统计了有互动的用户
SELECT AVG(session_duration_seconds) AS avg_duration
FROM user_sessions
WHERE DATE(session_start) = '2025-10-25';
-- 结果:180 秒(3 分钟)
-- 问题:数据看起来很好,但忽略了跳出用户
后果:
-
仪表板显示用户互动良好
-
实际上 40% 的用户立即跳出
-
产品经理做出了错误的优化决策
-- ✅ 正确查询:同时展示两个视角
SELECT
AVG(COALESCE(session_duration_seconds, 0)) AS avg_including_bounces,
AVG(session_duration_seconds) AS avg_excluding_bounces,
COUNT(*) AS total_sessions,
COUNT(session_duration_seconds) AS engaged_sessions
FROM user_sessions
WHERE DATE(session_start) = '2025-10-25';
-- 结果展示:
-- avg_including_bounces: 108 秒(包含跳出)
-- avg_excluding_bounces: 180 秒(排除跳出)
-- total_sessions: 1,000
-- engaged_sessions: 600
-- 业务洞察:40% 跳出率需要优化落地页
3.3 错误案例 3:学生成绩统计
业务背景:
-
期末考试成绩统计
-
部分学生缺考(记录为 NULL)
-- ❌ 错误查询:只统计了参加考试的学生
SELECT AVG(test_score) AS avg_score
FROM test_results
WHERE exam_id = 'FINAL_2025';
-- 结果:78 分
-- 问题:缺考学生应该计为 0 分,但被排除了

最低0.47元/天 解锁文章
211

被折叠的 条评论
为什么被折叠?



