SQL中实现年度累计和需先提取年份字段再PARTITION BY,且必须配合ORDER BY保证顺序;不同数据库年份提取函数各异,NULL值与跨年日期需特别处理。

SQL里用PARTITION BY按年份做年度累计和,核心是窗口函数+日期截断
直接结论:不能只写 PARTITION BY YEAR(date_col) —— 多数数据库不支持对表达式直接用 YEAR() 函数(比如 PostgreSQL、Snowflake),MySQL 虽支持但隐含时区/零值风险;更稳妥的做法是先提取年份字段(或用日期范围截断),再配合 ORDER BY 做有序累计。
不同数据库提取年份的写法差异很大
年份提取不是语法糖,它直接影响 PARTITION BY 是否合法、结果是否可预期:
- MySQL:可用
YEAR(order_date)或DATE_FORMAT(order_date, '%Y'),但注意NULL日期会导致该行被排除在窗口外 - PostgreSQL:必须用
EXTRACT(YEAR FROM order_date)或TO_CHAR(order_date, 'YYYY');EXTRACT返回数值,TO_CHAR返回字符串,两者在PARTITION BY中行为一致,但排序稳定性略有差别 - BigQuery:推荐
EXTRACT(YEAR FROM order_date),避免FORMAT_DATE('%Y', order_date)在跨时区场景下出错 - SQL Server:用
YEAR(order_date)安全,但若列含datetime2且精度高,建议先CAST(order_date AS DATE)再取年,防止边界日(如 2023-12-31 23:59:59.999)因毫秒进位误入下一年
累计求和必须加ORDER BY,否则结果不可靠
只写 SUM(amount) OVER (PARTITION BY EXTRACT(YEAR FROM order_date)) 是“年度总和”,不是“年度累计和”。要累计,必须显式指定顺序:
- 累计和本质是“到当前行为止的年内的累加”,所以
ORDER BY order_date是刚需 - 如果业务要求按订单编号排序而非日期,就用
ORDER BY order_id,但得确保order_id在年内单调递增,否则逻辑会混乱 - 存在同一天多笔记录时,仅靠
order_date不足以确定稳定顺序,应补上二级排序,例如:ORDER BY order_date, order_id - 示例(PostgreSQL):
SELECT order_date, amount, SUM(amount) OVER ( PARTITION BY EXTRACT(YEAR FROM order_date) ORDER BY order_date, order_id ) AS yearly_cumsum FROM sales;
容易被忽略的 NULL 和边界问题
实际数据中,这几个点常导致累计值跳变或中断:
-
order_date IS NULL的行会被归入同一组(多数引擎视 NULL 为相同值),但窗口函数对 NULL 排序位置不保证,可能出现在最前或最后,建议提前过滤或用COALESCE(order_date, '1970-01-01'::DATE)显式控制 - 跨年订单(如订单日期是 2023-12-31,发货日期是 2024-01-02)—— 累计逻辑必须明确基于哪个日期字段,混用会导致年度分组错乱
- 使用字符串年份(如
'2023')做PARTITION BY时,若某年数据缺失(比如没 2022 年数据),窗口函数不会“留空”,而是直接跳过该年,这点和 GROUP BY 不同,需在应用层补零
年度累计看着简单,真正跑通要同时盯住日期处理方式、窗口排序稳定性、NULL 行归属这三处。漏掉任一,结果就不是“累计”,只是碰巧看起来像。

















