
滑动窗口计算必须显式定义ROWS或RANGE
直接写 OVER(ORDER BY column) 不会触发滑动窗口,它只产生逻辑顺序,实际窗口默认是 UNBOUNDED PRECEDING TO CURRENT ROW —— 这是累积计算(如累计求和),不是你想要的“固定长度滑动”。要算最近3条记录的平均值,必须明确限定范围。
- 用
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW表示“当前行 + 前2行”,共3行 - 用
RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW适合时间序列,但要求排序列是日期类型且无重复值,否则可能意外包含多行 -
ROWS按物理行数切片,更可控;RANGE按值域切片,易受重复排序键影响
ORDER BY列必须有确定性,否则结果不可复现
如果 ORDER BY timestamp 存在毫秒级相同值,数据库无法保证相同时间戳的行之间顺序,滑动窗口可能每次执行取到不同子集。这不是bug,是SQL标准允许的行为。
- 补全排序:改用
ORDER BY timestamp, id(id为主键或唯一递增字段) - 避免用
ORDER BY RAND()或表达式(如ORDER BY UPPER(name))做滑动窗口,它们不提供稳定偏序 - PostgreSQL 中可加
NULLS LAST显式控制空值位置,防止隐式排序干扰窗口边界
窗口函数不能嵌套,滑动聚合需一步到位
不能写 AVG(SUM(value) OVER (...)) OVER (...) —— 大多数数据库(包括 PostgreSQL、SQL Server、BigQuery)直接报错 Window function cannot be used as argument to another window function。
- 想算“每小时滑动3小时的销售额均值”,先按小时聚合:
SUM(sales) AS hourly_sales,再在其上套AVG(hourly_sales) OVER (ORDER BY hour ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) - MySQL 8.0+ 支持 CTE,推荐先
WITH hourly AS (...) SELECT ..., AVG(...) OVER (...) FROM hourly,逻辑清晰且易调试 - 注意数据类型:整数列做
AVG()可能截断小数,显式转成DECIMAL或FLOAT
性能敏感点:PARTITION BY + ORDER BY 组合决定执行计划
没有 PARTITION BY 的全局滑动窗口(如全表按时间滑动)必须扫描并排序全部数据,无法利用索引加速;加上分区后,每个分区独立排序,才可能走索引。
- 高频查询场景下,给
(product_id, event_time)建复合索引,配合PARTITION BY product_id ORDER BY event_time,可让窗口计算落在索引范围内 - SQLite 不支持窗口函数的
PARTITION BY,强行用会报错no such function: ROW_NUMBER(实际是语法不识别),得换用自连接或子查询模拟 - BigQuery 中
ORDER BY列若为DATE类型而非TIMESTAMP,RANGE窗口可能因时区处理异常漏数据
ORDER BY 列存在重复值或空值时,ROWS 和 RANGE 的行为差异会直接导致业务指标偏差。











