如何在MySQL中实现分组内的累计求和(Running Total)
没问题!我来帮你梳理下在MySQL里实现分组累计求和(Running Total)的两种方法,同时满足你排除每个Id对应最大月份记录的需求。
先把你的输入数据贴出来,方便测试验证:
Create table Employee (Id int, Month int, Salary int); insert into Employee (Id, Month, Salary) values ('1', '1', '20'); insert into Employee (Id, Month, Salary) values ('2', '1', '20'); insert into Employee (Id, Month, Salary) values ('1', '2', '30'); insert into Employee (Id, Month, Salary) values ('2', '2', '30'); insert into Employee (Id, Month, Salary) values ('3', '2', '40'); insert into Employee (Id, Month, Salary) values ('1', '3', '40'); insert into Employee (Id, Month, Salary) values ('3', '3', '60'); insert into Employee (Id, Month, Salary) values ('1', '4', '60'); insert into Employee (Id, Month, Salary) values ('3', '4', '70');
方法一:MySQL 8.0+ 用窗口函数(推荐)
如果你用的是MySQL 8.0或更新的版本,直接用窗口函数就能轻松实现,还能把你原来的SQL优化得更简洁——不用额外左连接子查询,直接用窗口函数获取每个Id的最大月份:
SELECT Id, Month, SUM(Salary) OVER(PARTITION BY Id ORDER BY Month) AS cumm_sal FROM Employee WHERE Month != (MAX(Month) OVER(PARTITION BY Id)) ORDER BY Id, Month DESC;
逻辑拆解:
SUM(Salary) OVER(PARTITION BY Id ORDER BY Month):按Id分组,在每个组内按Month从小到大排序,累计计算薪资总和,这就是你要的累计求和。MAX(Month) OVER(PARTITION BY Id):同样按Id分组,直接获取每个用户的最大月份,避免了额外的子查询连接。WHERE Month != ...:过滤掉每个用户最大月份的记录。- 最后
ORDER BY Id, Month DESC:让每个用户的记录按月份倒序排列,和你的预期输出一致。
运行这段SQL后,就能得到你想要的结果:
| Id | Month | cumm_sal | |----|-------|----------| | 1 | 3 | 90 | | 1 | 2 | 50 | | 1 | 1 | 20 | | 2 | 1 | 20 | | 3 | 3 | 100 | | 3 | 2 | 40 |
方法二:MySQL 5.x 版本(不支持窗口函数)
如果你的MySQL版本是5.x(不支持窗口函数),那就得用自连接+分组求和的方式来实现:
SELECT e1.Id, e1.Month, SUM(e2.Salary) AS cumm_sal FROM Employee e1 JOIN Employee e2 ON e1.Id = e2.Id AND e2.Month <= e1.Month JOIN (SELECT Id, MAX(Month) AS max_month FROM Employee GROUP BY Id) emax ON e1.Id = emax.Id WHERE e1.Month != emax.max_month GROUP BY e1.Id, e1.Month ORDER BY e1.Id, e1.Month DESC;
逻辑拆解:
- 子查询
emax:先获取每个Id对应的最大月份,用来后续过滤。 - 自连接
e1和e2:让每条记录e1关联同一个Id下所有月份小于等于它的记录e2。 SUM(e2.Salary):对关联到的所有e2的薪资求和,就得到了e1这条记录的累计薪资。WHERE e1.Month != emax.max_month:过滤掉最大月份的记录。- 最后分组排序,得到预期结果。
这个方法虽然代码长一点,但在老版本MySQL里是可行的,运行结果和上面的窗口函数方法完全一致。
内容的提问来源于stack exchange,提问作者Teja
相关产品推荐
相关产品推荐

