该用 VAR_POP 还是 VAR_SAMP 取决于统计目标:全体数据方差用 VAR_POP(除以 n),样本估计总体方差用 VAR_SAMP(除以 n−1);常见错误是抽样数据误用 VAR_POP 导致低估,单值分组时 VAR_POP 返回 0 无意义,窗口函数中需注意 ORDER BY 引发的累积计算陷阱。

VAR_POP 和 VAR_SAMP 到底该用哪个?
直接说结论:如果要算「全体数据的方差」,用 VAR_POP;如果样本来自更大总体、想估计「总体方差」,用 VAR_SAMP。两者公式分母不同:VAR_POP 除以 n,VAR_SAMP 除以 n-1(贝塞尔校正)。
常见错误是把抽样数据硬套 VAR_POP,结果低估方差——比如你只查了 100 条订单,却用 VAR_POP 去“代表全站订单波动”,偏差会明显。
-
VAR_POP适合:全量表统计(如“2024 年所有用户年龄方差”) -
VAR_SAMP适合:带 LIMIT 的分析、子查询结果、ETL 中间表等不确定是否覆盖总体的场景 - MySQL 8.0+、PostgreSQL、Oracle 都支持二者;SQLite 只有
variance()(行为等价于VAR_SAMP)
NULL 值不参与计算,但容易误判成“没数据”
VAR_POP 和 VAR_SAMP 会自动跳过 NULL 值,这点没问题;但问题常出在「整列都是 NULL」或「WHERE 筛选后无有效值」时,函数返回 NULL 而不是 0——这容易被当成 SQL 执行失败或数据异常。
比如你写 SELECT VAR_POP(score) FROM users WHERE status = 'inactive',结果返回 NULL,其实只是这批用户没填 score,不是函数报错。
- 检查是否真无数据:加
COUNT(score)对比,确认非空值数量 - 需要默认值时,用
COALESCE(VAR_POP(score), 0),但注意 0 和 NULL 语义不同(前者是“方差为 0”,后者是“无法计算”) - PostgreSQL 中可配合
HAVING COUNT(score) > 1过滤掉无效分组,避免单值导致方差为 0 的误导
GROUP BY 下的方差容易忽略“单值组”的陷阱
当你对分组数据求方差,比如按地区算订单金额方差,如果某地区只有 1 笔订单,VAR_SAMP 会返回 NULL(因为 n−1=0,除零未定义),而 VAR_POP 会返回 0——但这 0 毫无统计意义,只是数学上成立。
这种“单样本组”的方差值不能反映波动性,强行纳入平均或图表会扭曲整体结论。
- 先过滤:在
GROUP BY后加HAVING COUNT(amount) > 1 - 或改用
STDDEV_POP/STDDEV_SAMP辅助判断——标准差为 0 且计数为 1 时,大概率是假稳定 - MySQL 中若开启
sql_mode=STRICT_TRANS_TABLES,VAR_SAMP在单值组会报错,反而更早暴露问题
窗口函数里用 VAR_POP 要小心 ORDER BY 的隐含影响
写 VAR_POP(amount) OVER (PARTITION BY region ORDER BY time) 时,ORDER BY 会让窗口变成「从第一行到当前行」的累积窗口,而非整个分区——方差值会随时间推移不断变化,不是该地区的全局方差。
这是最隐蔽的坑:语法完全合法,结果也“看起来合理”,但业务含义已偏移。
- 要整个分区内方差,必须显式写
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING - 或者干脆去掉
ORDER BY(此时默认是整分区) - PostgreSQL 支持
VAR_POP(amount) OVER (PARTITION BY region)无ORDER BY写法,MySQL 8.0+ 也支持,但老版本需补全ROWS子句
方差本身不难算,难的是每次调用前得问一句:这个“总体”到底指什么——数据库里的表?业务定义的集合?还是你刚 LIMIT 出来的那几条?漏掉这一层,数字再准也没用。