结论:用窗口函数OVER配合RANGE BETWEEN INTERVAL是实现时间滑动窗口的主流可靠方式,但需满足时间字段为TIMESTAMP/DATE、统一时区、无NULL值及时间列有索引四个前提;ROWS BETWEEN仅按行数滑动,无法准确表达“过去N天”语义。

直接说结论:用窗口函数 OVER 配合 RANGE BETWEEN INTERVAL(或 ROWS BETWEEN)是主流且可靠的方式,但必须注意时间字段类型、时区、空值和索引支持这四个硬性前提。
为什么 ROWS BETWEEN 不能替代时间滑动窗口?
很多人误以为用 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 就能实现“过去7天”的统计,其实这是按行数滑动,不是按时间滑动。一旦某天无数据,窗口就跳过该日;若某天数据暴增,又会把大量同日记录全塞进窗口——结果完全偏离业务语义。
- 适用场景:仅当数据严格按天均匀分布、且每日只有一条记录时才勉强可用
- 典型错误现象:
AVG(sales) OVER (ORDER BY dt ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)在缺失日期时返回“前6个非空日期”的均值,而非“最近7个自然日” - 正确做法:必须用
RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW,前提是dt是TIMESTAMP或DATE类型
RANGE BETWEEN INTERVAL 的实际写法与限制
PostgreSQL 和 Snowflake 原生支持 RANGE + INTERVAL,但 MySQL 8.0+ 仅支持 RANGE 用于数值列,不支持时间间隔;BigQuery 则要求用 UNBOUNDED PRECEDING + QUALIFY 配合子查询模拟。
- PostgreSQL 示例:
SELECT dt, SUM(revenue) OVER (ORDER BY dt RANGE BETWEEN INTERVAL '29 days' PRECEDING AND CURRENT ROW) AS sum_30d FROM sales - MySQL 替代方案:需先生成连续日期序列(
WITH RECURSIVE),再LEFT JOIN填充空日,最后用ROWS窗口(性能差,慎用) - 关键限制:
ORDER BY字段必须是单调递增的时间列,且不能为NULL;否则窗口行为未定义 - 性能影响:数据库需对时间列建立索引,否则
RANGE窗口可能触发全表扫描
如何处理时区与数据延迟带来的偏移?
真实业务中,“过去7天”往往指“本地营业日”,不是 UTC 时间戳的机械回溯。如果原始 created_at 存的是 UTC,直接套 INTERVAL '6 days' 会跨错日界。
- 推荐做法:在
OVER子句前统一转换时区,例如ORDER BY created_at AT TIME ZONE 'Asia/Shanghai' - 数据延迟场景(如凌晨2点跑昨日统计):用
CURRENT_DATE - INTERVAL '1 day'作为逻辑截止点,再用WHERE dt 过滤,避免把未落地数据纳入窗口 - 容易踩的坑:
AT TIME ZONE在不同数据库语法不同(PostgreSQL 支持,MySQL 不支持),务必查证目标引擎文档
时间滑动窗口最隐蔽的复杂点不在语法,而在数据质量——只要存在重复时间戳、时钟漂移、或批量补录导致的时间乱序,RANGE 窗口就会聚合到错误范围。上线前务必用小时间粒度抽样验证边界值。

















