LeetCode Restaurant Growth:SQL窗口函数ROWS/RANGE用法及结果异常求助
解法思路与优化代码
核心问题分析
你遇到的两个问题根源都是未先对日期进行聚合:
- 原表同一日期存在多行数据时,
ROWS BETWEEN是按行计数而非按日期计数,导致窗口范围错误(比如同一天3条记录,6 preceding会取当天3条+前3条,而非前6天)。 - 直接在原表加
WHERE过滤,窗口函数会基于过滤后的行计算,而非完整的日期序列,结果自然异常。
正确解法步骤
- 先聚合每日销售额:将同一日期的多条记录合并为一行,确保窗口函数处理的是「每日数据」而非「每条订单数据」。
- 用日期范围型窗口计算:使用
RANGE BETWEEN结合时间间隔,精准匹配「包含当前日期在内的连续7天」,而非按行计数。 - 过滤有效记录:只保留存在完整7天窗口的日期(即从第7天开始)。
优化代码(以MySQL为例)
WITH daily_sales AS ( -- 第一步:按日期聚合,得到每日总销售额 SELECT visited_on, SUM(amount) AS daily_amount FROM customer GROUP BY visited_on ) SELECT visited_on, -- 计算连续7天销售额总和 SUM(daily_amount) OVER ( ORDER BY visited_on RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW ) AS amount, -- 计算连续7天平均销售额(保留两位小数) ROUND(AVG(daily_amount) OVER ( ORDER BY visited_on RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW ), 2) AS average_amount FROM daily_sales -- 只保留有完整7天窗口的日期 WHERE visited_on >= (SELECT MIN(visited_on) + INTERVAL 6 DAY FROM customer) ORDER BY visited_on;
关键细节说明
RANGE BETWEEN的正确语法:针对日期类型,需用INTERVAL指定时间范围,该写法支持MySQL 8.0+、PostgreSQL等主流数据库(PostgreSQL语法为RANGE BETWEEN '6 days'::INTERVAL PRECEDING AND CURRENT ROW)。- 为什么先聚合:确保窗口函数的每一行对应一个唯一日期,彻底解决同一日期多行导致的窗口范围错误。
- 过滤条件的正确性:用
MIN(visited_on) + INTERVAL 6 DAY代替datediff,避免原表日期不连续时的判断误差。
替代方案(日期连续场景)
如果确定原表日期是连续无缺失的,也可以用ROWS BETWEEN,但仍需先聚合:
WITH daily_sales AS ( SELECT visited_on, SUM(amount) AS daily_amount, ROW_NUMBER() OVER (ORDER BY visited_on) AS rn FROM customer GROUP BY visited_on ) SELECT visited_on, SUM(daily_amount) OVER (ORDER BY rn ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS amount, ROUND(AVG(daily_amount) OVER (ORDER BY rn ROWS BETWEEN 6 PRECEDING AND CURRENT ROW), 2) AS average_amount FROM daily_sales WHERE rn >=7 ORDER BY visited_on;
内容的提问来源于stack exchange,提问作者Aditya N Panchal
相关产品推荐
相关产品推荐

