Spark SQL如何按指定日期计算过去30天(不含)的数值总和
解决Spark SQL计算过去30天滑动总和的问题
我看你遇到的问题是用窗口函数计算每个门店商品过去30天(不含当日)的总和时,原SQL语法不对导致没得到预期结果。先帮你拆解一下问题,再给出正确的解决方案:
原SQL的问题
你的原SQL里,窗口函数的ORDER BY a.date BETWEEN ...是语法错误——窗口函数的ORDER BY只是用来指定窗口内的排序字段,窗口的范围需要单独用ROWS BETWEEN或RANGE BETWEEN来定义,不能直接在ORDER BY后面加条件。
正确的Spark SQL写法
Spark 2.3及以上版本支持直接用INTERVAL语法定义基于时间的滑动窗口,正好匹配你的需求。这里我们需要计算当前日期往前推30天到前1天的总和(不含当日),所以窗口范围应该是RANGE BETWEEN INTERVAL 30 DAY PRECEDING AND INTERVAL 1 DAY PRECEDING。
基础版本(仅针对已有记录)
如果你的原表中已经包含所有需要的日期-门店-商品组合记录,可以直接用下面的SQL:
SELECT date, store, product, -- 计算过去30天(不含当日)的values总和 SUM(values) OVER ( PARTITION BY store, product -- 按门店和商品分组 ORDER BY date -- 按日期排序 RANGE BETWEEN INTERVAL 30 DAY PRECEDING AND INTERVAL 1 DAY PRECEDING ) AS sum_30d FROM your_table_name;
完整版本(生成所有日期-门店-商品记录)
如果原表中存在某些日期没有对应门店商品的记录(比如2020-08-01没有Store1|Product1的条目),上面的SQL不会生成该日期的记录。这时候需要先构建日期、门店、商品的全量维度,再左连接原表,确保每个组合都有数据:
WITH date_dim AS ( -- 生成需要的日期范围,这里以2020-06-01到2020-08-01为例 SELECT sequence(to_date('2020-06-01'), to_date('2020-08-01'), INTERVAL 1 DAY) AS dates LATERAL VIEW explode(dates) AS date ), store_product_dim AS ( -- 获取所有唯一的门店-商品组合 SELECT DISTINCT store, product FROM your_table_name ), full_dim AS ( -- 生成日期-门店-商品的全量组合 SELECT d.date, sp.store, sp.product FROM date_dim d CROSS JOIN store_product_dim sp ), joined_data AS ( -- 左连接原表,没有数据的values填0 SELECT fd.date, fd.store, fd.product, COALESCE(t.values, 0) AS values FROM full_dim fd LEFT JOIN your_table_name t ON fd.date = t.date AND fd.store = t.store AND fd.product = t.product ) SELECT date, store, product, SUM(values) OVER ( PARTITION BY store, product ORDER BY date RANGE BETWEEN INTERVAL 30 DAY PRECEDING AND INTERVAL 1 DAY PRECEDING ) AS sum_30d FROM joined_data ORDER BY date, store, product;
验证示例
拿你提到的例子来说:
- 当日期是2020-08-01时,窗口范围是2020-07-02到2020-07-31(因为8月1日减30天是7月2日,减1天是7月31日),如果这段时间内Store1|Product1的values总和是5,结果就会显示
2020-08-01|Store1|Product1|5,完全符合你的预期。
内容的提问来源于stack exchange,提问作者PauloHFM




