SQL如何对比不同时间窗口的指标_滑动窗口与聚合函数的结合

2次阅读

LAG() 和 LEAD() 适合轻量级相邻时间对比,但需 ORDER BY 唯一排序;非连续日期应 JOIN+DATE_SUB 对齐;滑动窗口须用 ROWS BETWEEN 并配合 PARTITION BY 分组。

SQL 如何对比不同时间窗口的指标_滑动窗口与聚合函数的结合

LAG()LEAD() 做相邻时间窗口对比最直接

想比“昨天 vs 今天”“上周 vs 本周”,不用建多个子查询或临时表,LAG()LEAD() 是最轻量、可读性最好的选择。它们本质是按排序取上 / 下一行的值,天然适配时间序列对比场景。

常见错误是没写 ORDER BY 或排序字段不唯一:比如只按 date 排序,但同一天有多条记录,结果会随机偏移。必须确保 ORDER BY 能唯一确定行序,例如 ORDER BY date, id 或加 ROW_NUMBER() 辅助。

  • LAG(value, 1) 取前 1 行的 value,适合“昨日值”“上周值”
  • LAG(value, 7) 直接跳 7 行,适合“7 天前值”,但前提是数据每天一条且无断更;否则要用日期计算对齐(见下一条)
  • 如果时间不连续(如跳过周末、节假日),优先用 JOINDATE_SUB() 关联精确日期,而不是依赖行偏移

DATE_SUB() + LEFT JOIN 对齐非连续时间窗口

业务中“上周同日”“去年同期”往往不是简单往前数几行,而是按日历语义对齐。这时靠 LAG() 会出错——比如周五的“上周五”可能因数据缺失变成周四的值。

正确做法是把原表和自身按日期做 LEFT JOIN,用 DATE_SUB(t1.date, INTERVAL 7 DAY) 匹配目标日期。这样哪怕中间缺数据,也能明确知道“该比谁”。

  • MySQL / Spark SQL 支持 DATE_SUB(date, INTERVAL N DAY);PostgreSQL 用 date - INTERVAL 'N days'
  • JOIN 条件要写成 t1.date = DATE_SUB(t2.date, INTERVAL 7 DAY),而不是反过来,避免 NULL 漏匹配
  • 记得给日期字段建索引,否则自连接性能会随数据量陡增

AVG() 窗口函数里嵌套 ROWS BETWEEN 控制滑动范围

要做“过去 30 天滚动均值”,不能只靠 AVG(value) OVER (ORDER BY date)——默认是累计平均(从第一行算到当前),不是滑动。

必须显式指定窗口帧:ROWS BETWEEN 29 PRECEDING AND CURRENT ROW。数字是“行数”,不是“天数”,所以前提仍是数据按天连续、每日一行。

  • 如果某天没数据,那一行就没了,窗口自动变短,结果偏高;想严格按日历滚动,得先用 GENERATE_SERIES(PostgreSQL)或递归 CTE 补全日期
  • MySQL 8.0+ 支持 ROWS,但不支持 RANGE 按值范围滑动(如“date >= CURRENT_DATE – 30”),别混淆
  • CURRENT ROW 包含当前行,UNBOUNDED PRECEDING 才是累计,别写反

聚合后套窗口函数容易漏掉 PARTITION BY 导致全局误算

当指标本身需要先分组聚合(比如“每个城市每日订单量”),再算“该城市近 7 日均值”,很多人会写成:

SELECT city, date, AVG(cnt) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) FROM (…… GROUP BY city, date) t

这实际是把所有城市的值混在一起排序滑动,结果完全错乱。

  • 必须加 PARTITION BY city,让窗口在每个城市内独立计算
  • 如果还有渠道、品类等维度,PARTITION BY 要包含全部分组键,和前面 GROUP BY 一致
  • 顺序很重要:PARTITION BY 必须在 ORDER BY 前面,SQL 标准语法不接受反过来写

时间窗口对比真正难的不是函数怎么写,而是想清楚“比的是什么粒度”和“时间对齐依据是什么”。数据断更、多源异步、业务定义漂移,这些都会让看似正确的 SQL 返回误导性结果。

星耀云
版权声明:本站原创文章,由 星耀云 2026-03-18发表,共计1573字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources