SQL中STDDEV和VARIANCE默认计算样本方差与标准差(分母n−1),PostgreSQL、Oracle、Snowflake均如此;MySQL中二者等价于STDDEV_SAMP和VAR_SAMP;SQL Server不支持该命名,须用STDEV/STDEVP。

SQL 里直接用 STDDEV 和 VARIANCE 就能算分组后的标准差与方差,但不同数据库对函数名、空值处理和样本/总体语义的实现差异极大,不注意会得出错误结果。
STDDEV 和 VARIANCE 在主流数据库中的函数名差异
不是所有数据库都叫 STDDEV 和 VARIANCE —— 这俩是 ANSI 标准名,但实际支持情况混乱:
- PostgreSQL:支持
STDDEV_POP(总体)、STDDEV_SAMP(样本),STDDEV是STDDEV_SAMP的别名;VARIANCE同理指向VARIANCE_SAMP - MySQL:只提供
STDDEV_POP、STDDEV_SAMP、VAR_POP、VAR_SAMP;STDDEV和VARIANCE是STDDEV_SAMP和VAR_SAMP的别名(5.7+) - Oracle:
STDDEV默认是样本标准差(即STDDEV_SAMP),STDDEV_POP才是总体;VARIANCE同理 - SQL Server:没有
STDDEV或VARIANCE,必须用STDEV/STDEVP和VAR/VARP
所以写跨库 SQL 时,别图省事写 STDDEV,明确写 STDDEV_SAMP 或 STDDEV_POP 更安全。
分组计算时 NULL 值被自动忽略,但影响分母
STDDEV_SAMP 和 VARIANCE_SAMP 的分母是 n-1(n 为非 NULL 行数),不是原始分组行数。如果某组有大量 NULL,n 可能小到让结果失真甚至报错(如 n=1 时 STDDEV_SAMP 返回 NULL)。
- 检查数据质量:先用
COUNT(*)和COUNT(column)对比,确认缺失比例 - 避免意外 NULL:可加
WHERE column IS NOT NULL预过滤,或用COALESCE(column, 0)填充(需业务允许) - 注意:填充后改变了统计口径,
STDDEV_SAMP的分母变成填充后的总行数,不再是原始非空样本量
样本 vs 总体:选错会导致结果系统性偏差
假设你有一张销售表 sales,按地区分组算订单金额的标准差:
SELECT region, STDDEV_SAMP(amount) AS std_samp, STDDEV_POP(amount) AS std_pop FROM sales GROUP BY region;
若该表已包含全部地区全部订单(即你研究的就是这一个总体,不打算推断更大群体),该用 STDDEV_POP;若这只是抽样数据,想估计整体分布,则必须用 STDDEV_SAMP。两者数值差异在小样本下明显——比如某地区仅 3 条订单:STDDEV_SAMP 分母为 2,STDDEV_POP 分母为 3,结果相差约 22%。
- 常见误用场景:报表系统默认用
STDDEV(即样本版),但业务方理解为“本地区全部数据的标准差”,导致解读偏差 - 验证方法:手动算一组小数据(如 [100, 200, 300]),对比数据库返回值与手算
√(Σ(x−x̄)² / (n−1))或/ n
性能与 NULL 处理的隐含开销
STDDEV_SAMP 和 VARIANCE_SAMP 需两遍扫描:第一遍算均值,第二遍算平方差和。在大数据集上比 COUNT 或 AVG 明显慢;若列上有大量 NULL,引擎仍要逐行判断,不能跳过。
- 高频查询建议:对稳定维度(如每日分区)预计算并存入物化视图或汇总表
- 替代思路:若只需相对离散程度,可用
MAX(col) - MIN(col)或PERCENTILE_CONT(0.75) - PERCENTILE_CONT(0.25)(四分位距),它们单次扫描且对 NULL 更鲁棒 - 特别注意:MySQL 5.7 以前的
STDDEV函数在 GROUP BY 中可能触发临时表 + 文件排序,8.0+ 改进明显,但仍有代价
真正容易被忽略的是:标准差本身对异常值极度敏感,而 SQL 聚合函数不会自动剔除离群点。分组后某个值突然飙升,整组标准差就失真——得先做业务侧的合理性校验,而不是无条件信任 STDDEV 的输出。











