用SUM() OVER()计算年度累计销售额必须正确设置PARTITION BY YEAR(sale_date)和ORDER BY sale_date,并先按日聚合再开窗,显式指定ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,且需过滤NULL和非法日期。
直接用 SUM() OVER() 就能算年度累计销售额,但必须确保分区、排序和日期范围都对,否则结果会错得毫无征兆。
年度累计不是全表累加,而是按年分组后在年内逐月累加。所以 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_sumFROM ( 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) 更可靠,避免字符串比较陷阱。
这是默认行为,但显式写出更安全。SQL Server 默认窗口帧是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,即从当年第一行到当前行。如果误写成 RANGE(尤其在日期有重复时),可能因隐式去重导致累计值跳变。
ROWS:严格按物理行顺序累加,稳定RANGE:相同 sale_date 的所有行被视作同一组,累计值会在该日期首次出现时“跳涨”最易被忽略的是数据清洗环节:销售日期字段里混入 NULL 或非法日期(如 '9999-01-01'),会导致 YEAR() 返回 NULL,整年数据被归入同一分区,累计逻辑彻底失效。上线前务必加 WHERE sale_date IS NOT NULL AND ISDATE(sale_date) = 1 过滤。