用sum() over()计算年度累计销售额必须正确设置partition by year(sale_date)和order by sale_date,并先按日聚合再开窗,显式指定rows between unbounded preceding and current row,且需过滤null和非法日期。

直接用 SUM() OVER() 就能算年度累计销售额,但必须确保分区、排序和日期范围都对,否则结果会错得毫无征兆。
窗口函数的 PARTITION BY 和 ORDER BY 怎么配?
年度累计不是全表累加,而是按年分组后在年内逐月累加。所以 PARTITION BY YEAR(sale_date) 是必须的,ORDER BY sale_date 决定累加顺序——如果用 ORDER BY product_id 或漏写 ORDER BY,累计值就变成乱序或单行值。
-
PARTITION BY错:漏掉年份,会导致跨年累加(比如 2023 年 12 月 + 2024 年 1 月) -
ORDER BY错:用ORDER BY amount会让销量高的月份先累加,失去时间维度意义 - 日期字段必须是
DATE或DATETIME类型,不能是字符串,否则YEAR()提取可能出错或隐式转换失败
如何处理同一天多笔订单?
真实业务中一天常有多个订单,直接对原始明细行做 SUM(amount) OVER(...) 会重复累加。正确做法是先按日聚合,再开窗:
SELECT
sale_date,
daily_total,
SUM(daily_total) OVER (
PARTITION BY YEAR(sale_date)
ORDER BY sale_date
) AS cum_sum
FROM (
SELECT
CAST(sale_time AS DATE) AS sale_date,
SUM(amount) AS daily_total
FROM sales
GROUP BY CAST(sale_time AS DATE)
) t
注意:CAST(sale_time AS DATE) 比 CONVERT(VARCHAR(7), sale_time, 120) 更可靠,避免字符串比较陷阱。
为什么用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW?
这是默认行为,但显式写出更安全。SQL Server 默认窗口帧是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,即从当年第一行到当前行。如果误写成 RANGE(尤其在日期有重复时),可能因隐式去重导致累计值跳变。
- 用
ROWS:严格按物理行顺序累加,稳定 - 用
RANGE:相同sale_date的所有行被视作同一组,累计值会在该日期首次出现时“跳涨” - 不要省略帧定义——虽然默认存在,但显式写出可避免不同 SQL Server 版本或兼容级别下的歧义
最易被忽略的是数据清洗环节:销售日期字段里混入 NULL 或非法日期(如 '9999-01-01'),会导致 YEAR() 返回 NULL,整年数据被归入同一分区,累计逻辑彻底失效。上线前务必加 WHERE sale_date IS NOT NULL AND ISDATE(sale_date) = 1 过滤。











