直接group by后不能用sum()再累加,因分组已丢失行级数据,需用sum() over窗口函数实现累积和;关键需指定partition by分组、order by排序,漏掉order by则得整组总和而非累计值。

为什么直接 GROUP BY 后不能用 SUM() 再累加?
因为 GROUP BY 会把多行压缩成一行,原始行级数据就丢失了。你没法在聚合后的结果上再做“逐组内逐行累加”——那已经不是分组逻辑,而是窗口逻辑。
比如按月份分组统计销售额后,想看“本年度截至当月的累计销售额”,这必须保留每组内的顺序和行结构,SUM() OVER 正是为此设计的。
SUM OVER 的基本写法和分区关键点
核心是明确三件事:累加什么、按什么分组(PARTITION BY)、按什么排序(ORDER BY)。漏掉 ORDER BY 会导致“整组总和”而非“累积和”。
-
SUM(sales) OVER (PARTITION BY year ORDER BY month)→ 每年内部按月递增累加 -
SUM(sales) OVER (PARTITION BY year)→ 每年一个固定总和,没累加效果 -
SUM(sales) OVER (ORDER BY date)→ 不分组,全表时间轴累加(慎用,易跨业务域)
常见错误:ORDER BY 字段不在 SELECT 中或含 NULL
某些数据库(如 PostgreSQL)允许 ORDER BY 字段未出现在 SELECT 列表,但 MySQL 8.0+ 和 SQL Server 要求它至少可被解析。更隐蔽的问题是:month 为 NULL 时,该行会排在最前或最后,导致累加起点错乱。
- 显式处理空值:
ORDER BY COALESCE(month, '1970-01') - 确保排序字段有索引,否则大表上
OVER性能陡降 - 避免用表达式排序(如
ORDER BY YEAR(date)),部分引擎无法利用索引
和 GROUP BY 混用时的典型场景
真正实用的模式往往是先分组聚合,再对聚合结果做窗口累加。例如:先算出每月销售额,再算年度累计。
SELECT
year, month,
monthly_sales,
SUM(monthly_sales) OVER (PARTITION BY year ORDER BY month) AS cum_sales
FROM (
SELECT
YEAR(order_date) AS year,
MONTH(order_date) AS month,
SUM(amount) AS monthly_sales
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date)
) t;
注意子查询里必须包含用于 PARTITION BY 和 ORDER BY 的字段,且类型一致——比如别把 YEAR() 结果当字符串再拼接,会导致分区断裂。










