stddev 和 variance 默认计算样本标准差和样本方差(分母为 n−1);若数据为总体,须显式使用 stddev_pop/variance_pop 或手动计算 sqrt(avg(xx)-avg(x)avg(x))。

STDDEV 和 VARIANCE 函数到底算的是总体还是样本?
SQL 标准里 STDDEV 和 VARIANCE 默认计算的是**样本标准差和样本方差**(即分母为 n−1),不是总体值。这点极易被误用,尤其当你的业务逻辑要求“整个群体的波动率”(比如全量用户日活的离散程度)时,直接套用会偏高。
不同数据库实现略有差异:
- PostgreSQL、Oracle、SQL Server(2022+)支持
STDDEV_POP/VARIANCE_POP(总体)和STDDEV_SAMP/VARIANCE_SAMP(样本)明确区分 - MySQL 8.0+ 同样提供这两组函数;5.7 及更早版本只有
STDDEV(=STDDEV_SAMP)和VAR_POP等别名,但命名不统一 - SQLite 只有
stddev(样本)和variance(样本),无总体版本
如何避免分母错误导致波动率被高估?
如果你的数据本身就是总体(例如:某天所有订单金额、某批次全部传感器读数),必须显式使用总体函数,否则结果偏差随样本量减小而加剧。比如 5 条记录时,样本标准差比总体标准差约大 12%。
实操建议:
- 先确认业务语义:是「抽样推断」还是「全量描述」?前者用
STDDEV_SAMP,后者强制用STDDEV_POP - 在不支持
_POP后缀的老版本 MySQL 中,可手动计算:SQRT(AVG(x*x) - AVG(x)*AVG(x))—— 这是总体标准差的等价写法(基于方差定义σ² = E[X²] − (E[X])²) - 注意 NULL 处理:
STDDEV类函数默认忽略 NULL 值,但若字段允许 NULL 且你希望将其视作 0 或报错,需提前用COALESCE或WHERE x IS NOT NULL
在 GROUP BY 场景下计算各分组波动率要注意什么?
当按时间、地区、品类分组看波动率时,最容易踩的坑是「组内记录数过少却仍用样本公式」。例如某地区只有 2 笔交易,STDDEV_SAMP 的分母为 1,结果对异常值极度敏感,几乎失去参考价值。
建议做法:
- 加过滤条件:
HAVING COUNT(*) > 3,排除不可靠的小样本组 - 同时输出计数与标准差:
SELECT region, STDDEV_SAMP(amount) AS std_amt, COUNT(*) AS cnt FROM sales GROUP BY region,人工判断 std_amt 是否可信 - 若需平滑处理,可改用变异系数(CV =
STDDEV_POP / AVG),它消除了量纲影响,更适合跨组比较波动相对强度
为什么有时 STDDEV 返回 NULL?
STDDEV 类函数在以下情况返回 NULL:
- 输入集合为空(如
WHERE FALSE后无行) - 输入集合只有一行(样本标准差定义要求至少两个值,因分母 n−1=0)
- 所有非 NULL 值都相等(此时方差为 0,标准差为 0 —— 这是正常数值,不是 NULL;只有前两种情况才返回 NULL)
遇到 NULL 时不要直接丢弃,应检查是否漏了 WHERE 条件或聚合粒度太粗。必要时用 COALESCE(STDDEV(x), 0) 转换,但得清楚这掩盖了数据稀疏问题。
真正难处理的不是函数怎么写,而是想清楚——你手上的数据,在当前分析目标下,究竟该被当作一个样本,还是一整个总体。










