如何在SQL中优雅实现多滚动窗口聚合后的Union操作
更简洁的滚动窗口求和方案
针对你这种固定长度的滚动窗口求和需求,完全不用写一堆UNION ALL,有两种更高效简洁的方式:
方法1:先生成窗口范围,再关联计算
先找出所有有效的窗口起始周(这里窗口长度是3周,所以起始周从1到最大周数-2),再和原表关联筛选对应范围的记录,最后聚合求和:
-- 先生成窗口的起始和结束周 WITH window_ranges AS ( SELECT week AS start_week, week + 2 AS end_week FROM sale GROUP BY week HAVING week + 2 <= (SELECT MAX(week) FROM sale) ) SELECT CONCAT(start_week, '-', end_week) AS period, SUM(s.sales) AS total_sales FROM window_ranges wr JOIN sale s ON s.week BETWEEN wr.start_week AND wr.end_week GROUP BY wr.start_week, wr.end_week ORDER BY wr.start_week;
这个方法的好处是不管窗口数量有多少,都只需要调整week + 2里的数字(对应窗口长度-1),就能自动生成所有有效窗口,不用手动写每个UNION分支。
方法2:先聚合每周销售额,再用滑动窗口函数
如果你的SQL支持窗口函数(比如MySQL 8.0+、PostgreSQL、SQL Server等),可以先按周聚合出每周的总销售额,再用滑动窗口求和:
-- 先计算每周的总销售额 WITH weekly_sales AS ( SELECT week, SUM(sales) AS weekly_total FROM sale GROUP BY week ) SELECT CONCAT(week, '-', week + 2) AS period, SUM(weekly_total) OVER( ORDER BY week ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING ) AS total_sales FROM weekly_sales WHERE week + 2 <= (SELECT MAX(week) FROM weekly_sales) ORDER BY week;
这种方式效率更高,因为先做了一次周聚合,减少了后续窗口计算的数据量。窗口函数里的2 FOLLOWING对应窗口长度为3(当前行+后面2行),调整这个数字就能适配不同的窗口长度。
对比你原来的写法,这两种方法都能避免重复编写大量重复的SELECT语句,当窗口数量变多的时候优势特别明显,也不会像笛卡尔积那样生成多余的行。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

