滑动标准差必须显式定义窗口帧,仅用partition by和order by会导致累积计算而非真正滑动;需根据需求选择rows或range并指定边界,且优先使用stddev_samp()。

滑动标准差不能只靠 STDDEV_SAMP() 加 PARTITION BY 就完事——加了 ORDER BY 但没配 ROWS 子句,结果就是滚动计算,不是你想要的“按部门静态分组标准差”。
滑动标准差必须显式声明窗口帧
写 STDDEV_SAMP(x) OVER (PARTITION BY dept ORDER BY hire_date) 看似合理,但数据库默认补上 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,每行算的是“从组头到当前行”的累积标准差,不是整组固定值。要真正实现滑动(比如最近7天、最近5条记录),必须自己定义帧边界。
- 滚动最近3条:用
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW - 滚动最近1小时(时序场景):配合
RANGE BETWEEN INTERVAL '1 hour' PRECEDING AND CURRENT ROW,但注意仅 PostgreSQL / BigQuery 支持RANGE+ 时间间隔,MySQL 8.0+ 不支持 - 想覆盖整组但又保留排序语义(如按时间排后取全量标准差):必须写
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,不能省略 - 漏写
ROWS是跨库迁移最大雷区:PostgreSQL 允许不写,SQL Server 强制要求,MySQL 8.0 报错
STDDEV_SAMP() 和 STDDEV_POP() 在滑动中差异更敏感
滑动窗口越小,n-1 和 n 的分母差异越明显。比如某部门连续3人入职,滑动窗口设为3行,STDDEV_SAMP() 分母是2,STDDEV_POP() 分母是3,结果能差出近30%。业务上几乎全部该用 STDDEV_SAMP(),除非你明确知道窗口内数据就是总体且无抽样偏差。
- 实时监控类场景(如每分钟电流波动):窗口是样本,必须用
STDDEV_SAMP() - 历史回滚分析(如“过去30天每天销售额的标准差”):每天一条记录,30天是全集?不,它仍是更大周期的样本,仍该用
STDDEV_SAMP() - Oracle 中
STDDEV()单行返回 0,而STDDEV_SAMP()返回NULL,滑动中首行易出空,别混用
NULL 值在滑动窗口里会被静默跳过,但影响窗口长度判断
STDDEV_SAMP() 遇到 NULL 直接不参与计算,但窗口帧的行数(ROWS)和时间范围(RANGE)照常计数。比如设 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW(共2行),若前一行是 NULL,实际只有一行有效数据,此时 STDDEV_SAMP() 返回 NULL(样本数
- 先查
COUNT(*)和COUNT(value_col)差值,确认隐性NULL比例 - 业务上
NULL表示缺失,就用WHERE value_col IS NOT NULL预过滤;表示“0”,就用COALESCE(value_col, 0) - 别依赖函数自动处理
NULL,尤其滑动窗口中,NULL位置会影响“当前行”在帧中的相对位置
不同数据库对滑动标准差的支持度差异极大
不是所有标称“支持窗口函数”的库都真能跑通滑动标准差。MySQL 8.0+ 要求显式写 STDDEV_SAMP() 或 STDDEV_POP(),STDDEV() 不被识别;TDengine 只支持 INTERVAL 窗口(如每小时聚合),不支持 ROWS 滑动;SQLite 完全不支持任何窗口版 STDDEV。
- PostgreSQL / BigQuery / Snowflake:全功能支持,
ROWS和RANGE都可用 - SQL Server:支持
ROWS,但RANGE仅支持数值列,不支持时间间隔 - Oracle:支持,但
STDDEV()在滑动中行为与STDDEV_SAMP()不一致,建议统一用后者 - MySQL 8.0+:必须写全名,且不支持
RANGE BETWEEN ... INTERVAL,只能靠时间字段转成数值再算
滑动标准差最易被忽略的点:它本质是“动态重分组”,每行的计算上下文都不同。哪怕语法跑通,也要用小数据集手动验算两行,确认分母、NULL 处理、帧边界是否符合预期——因为多数数据库不会报错,只会安静地返回错误结果。











