VAR_SAMP和STDDEV是计算样本方差和样本标准差的聚合函数,分母为n−1,适用于推断统计;STDDEV通常是STDDEV_SAMP的别名。

VAR_SAMP 和 STDDEV 是什么函数?它们算的是总体还是样本?
VAR_SAMP 和 STDDEV(或等价的 STDDEV_SAMP)是 SQL 标准中用于计算**样本方差**和**样本标准差**的聚合函数。注意:它们默认按「样本」处理,即分母为 n−1,不是 n。如果你误用它们去算「总体方差」,结果会偏大——尤其在小数据集上偏差明显。
常见错误现象:SELECT VAR_SAMP(score) FROM students 返回值比 Excel 的 VAR.S 一致,但和 VAR.P 不同;若你本意是描述整个班级(无抽样),该用 VAR_POP 和 STDDEV_POP。
- 样本类函数(推荐用于推断统计):
VAR_SAMP、STDDEV_SAMP、STDDEV(多数数据库中STDDEV是STDDEV_SAMP的别名) - 总体类函数(适合全量数据描述):
VAR_POP、STDDEV_POP - PostgreSQL / Oracle / MySQL 8.0+ / SQL Server 2022+ 都支持;SQLite 不支持,需手写公式
如何在 GROUP BY 中正确使用 VAR_SAMP 计算每组方差?
直接套用即可,但要注意 NULL 值会被自动忽略——这通常合理,但若字段本应非空却含 NULL,会导致组内有效计数 n 比预期小,进而影响 n−1 分母的稳定性。
SELECT dept, COUNT(*) AS total_cnt, COUNT(score) AS non_null_cnt, VAR_SAMP(score) AS var_sample, STDDEV(score) AS stddev_sample FROM employees GROUP BY dept;
使用场景:分析各部门绩效分数离散程度。若某部门 non_null_cnt ,<code>VAR_SAMP 返回 NULL(因无法计算样本方差),这是正确行为,不是 bug。
- 确保分组字段与聚合字段逻辑自洽(例如不能对时间维度分组后,再对非时序稳定的指标算方差)
- 若需过滤掉方差过小的组,可在 HAVING 中写
HAVING VAR_SAMP(score) > 0.1,但注意 NULL 不参与比较 - MySQL 5.7 及更早版本不支持窗口化
VAR_SAMP,如需组内标准化(z-score),得用子查询或变量模拟
为什么 STDDEV 返回值和手动 SQRT(VAR_SAMP(x)) 不完全相等?
浮点精度差异。虽然数学上 STDDEV(x) = SQRT(VAR_SAMP(x)),但数据库内部实现可能采用单程算法(如 Welford 方法)来同步计算均值与方差,减少累积误差;而两步写法 SQRT(VAR_SAMP(x)) 先算方差再开方,中间多一次舍入。
实测差异通常在 1e-13 量级,不影响业务判断,但在做单元测试断言或审计比对时,建议统一用原生函数而非手算。
- Oracle 和 PostgreSQL 对
STDDEV和SQRT(VAR_SAMP())的结果做了对齐,基本一致 - MySQL 8.0 在某些边界值(如全相同数字)下,
STDDEV可能返回 -0.0,而SQRT(VAR_SAMP())返回 0.0 - 如需最大一致性,且数据库支持,优先用
STDDEV_SAMP而非别名STDDEV
没有 VAR_SAMP 的旧数据库(如 MySQL 5.6)怎么替代?
只能手写公式:VAR_SAMP = (SUM(x*x) - SUM(x)*SUM(x)/COUNT(x)) / (COUNT(x) - 1)。注意括号顺序和类型转换——整数除法会导致截断。
SELECT dept, (SUM(score*score) - SUM(score)*SUM(score)/COUNT(score)) / (COUNT(score) - 1) AS var_samp_manual FROM employees WHERE score IS NOT NULL GROUP BY dept HAVING COUNT(score) > 1;
容易踩的坑:
- 漏加
HAVING COUNT(score) > 1,导致除零错误 - 没写
WHERE score IS NOT NULL,让SUM返回 NULL,整行失效 -
SUM(score)*SUM(score)可能溢出(尤其 score 是 BIGINT 且组很大),可改用AVG拆解:先算均值,再算平方偏差均值 ×n/(n−1)
这种写法性能不如原生函数,且不可移植;一旦升级数据库,应尽快切回 VAR_SAMP。










