MySQL 8中使用窗口函数计算UNION查询余额结果错误的问题排查与解决
MySQL 8中使用窗口函数计算UNION查询余额结果错误的问题排查与解决
你好呀,看了你的问题和测试数据,我立刻就找到问题根源啦!
问题原因分析
你当前使用的窗口子句ROWS BETWEEN 1 PRECEDING AND CURRENT ROW,作用是仅计算当前行与前一行的credit - debit之和,这和你想要的「从第一行开始累计到当前行的滚动余额」逻辑完全不符。
举个例子:第三行(table2中id=2的记录),你的SQL计算的是(0-100) + (0-50) = -150,但正确的累计应该是前面所有行的总和:100 - 50 - 100 = -50,这就是余额结果错误的核心原因。
解决方案
要实现「上一行余额 + 当前行credit - 当前行debit」的滚动累计逻辑,我们需要把窗口范围设置为从数据集的第一行到当前行。在MySQL中,当窗口函数指定ORDER BY时,默认的窗口范围就是ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,所以我们可以直接省略这个子句,或者显式写出。
正确的SQL代码如下:
SELECT id, credit, debit, company, SUM(credit - debit) OVER (ORDER BY id) AS balance FROM ( SELECT id, credit, debit, company FROM table1 UNION ALL SELECT id, credit, debit, company FROM table2 ) AS u WHERE company = 1 ORDER BY id;
验证结果
执行上述SQL后,得到的balance列会和你手动标注的「Correct Balance」完全一致:
| Id | Credit | Debit | Company | Balance |
|---|---|---|---|---|
| 1 | 100 | 0 | 1 | 100 |
| 1 | 0 | 50 | 1 | 50 |
| 2 | 0 | 100 | 1 | -50 |
| 2 | 200 | 0 | 1 | 150 |
| 3 | 0 | 50 | 1 | 100 |
| 4 | 100 | 0 | 1 | 200 |
| 7 | 50 | 0 | 1 | 250 |
| 8 | 0 | 200 | 1 | 50 |
额外注意事项
UNION ALL合并后的数据集,在未指定ORDER BY时顺序是不确定的,所以一定要在窗口函数的OVER子句中加上ORDER BY id,确保累计计算是按照id的顺序进行的,这是保证结果正确的关键。
备注:内容来源于stack exchange,提问作者Mehran Ishanian
相关产品推荐
相关产品推荐

