VAR_POP和STDDEV_POP计算总体统计量,需除以n;样本分析应使用VAR_SAMP和STDDEV_SAMP(除以n−1),MySQL 5.7需手动实现,且各数据库中STDDEV含义不一,应显式使用全称函数。

VAR_POP 和 STDDEV_POP 计算的是总体统计量,不是样本
很多用户看到 VAR_POP 和 STDDEV_POP 就默认“POP”是“population”的缩写,所以理所当然认为它们适合日常分析——但实际中,绝大多数 SQL 查询面对的都是样本(比如某天的订单、某批用户的点击日志),而非全部总体。直接用这两个函数会低估真实离散程度,因为它们除以 n(数据行数),而样本方差应除以 n-1。
关键区别:
-
VAR_POP(x)=SUM((x - AVG(x))²) / COUNT(*) -
VAR_SAMP(x)=SUM((x - AVG(x))²) / (COUNT(*) - 1)(这才是样本方差)
同理:STDDEV_POP 对应总体标准差,STDDEV_SAMP 才对应样本标准差。MySQL 8.0+、PostgreSQL、Oracle、SQL Server(2012+)都支持 VAR_SAMP 和 STDDEV_SAMP;旧版 MySQL(5.7 及以前)不支持,需手写计算。
MySQL 5.7 怎么手动算样本标准差
当数据库不支持 STDDEV_SAMP,又必须按样本公式计算时,得拆解:先算均值,再算平方偏差和,最后除以 n-1 开根号。注意 COUNT(*) 必须 ≥ 2,否则分母为 0 会报错 Division by zero。
实操建议:
- 用子查询或 CTE 避免重复计算
AVG(),防止因浮点误差导致结果偏移 - 对空值敏感:所有
VAR_*/STDDEV_*函数默认忽略NULL,手动写法也需加WHERE x IS NOT NULL - 示例(兼容 MySQL 5.7):
SELECT SQRT( SUM(POW(score - avg_score, 2)) / (COUNT(*) - 1) ) AS sample_stddev FROM ( SELECT score, AVG(score) OVER() AS avg_score FROM exam_results WHERE score IS NOT NULL ) t WHERE COUNT(*) > 1;
STDDEV 在不同数据库里到底指什么
STDDEV 这个函数名在各数据库中含义不一致,容易踩坑:
- MySQL:从 5.7 起,
STDDEV是STDDEV_SAMP的别名 → 样本标准差 - PostgreSQL:
STDDEV同样等价于STDDEV_SAMP - Oracle:
STDDEV默认是STDDEV_SAMP,但文档明确建议显式写全称 - SQL Server:没有
STDDEV,只有STDEV(= 样本)和STDEVP(= 总体)
所以跨库迁移时,不能只靠函数名判断行为。最稳妥的做法是:一律显式使用 STDDEV_SAMP 或 STDDEV_POP,避免依赖别名。
聚合时 NULL 和空组怎么影响结果
VAR_SAMP 和 STDDEV_SAMP 在输入少于 2 行非 NULL 值时,返回 NULL(不是报错)。这很合理:单个数无法定义“样本变异”,但容易被误读为“计算失败”。
常见陷阱:
- 用
GROUP BY分组后某组只有 1 条有效数据 → 该组STDDEV_SAMP为NULL,若后续参与AVG()或SUM(),可能意外剔除整组 -
WHERE过滤太严,导致分组后COUNT(*) = 1,却没意识到标准差已失效 - 想补默认值?别用
COALESCE(STDDEV_SAMP(x), 0)—— 0 会扭曲统计意义,更合适的是保留NULL并在应用层标注“N/A”
真正难处理的不是语法,而是搞清当前数据集到底算样本还是总体,以及是否满足 n ≥ 2 的前提。这个判断没法交给数据库自动做。










