MySQL累计求和出现多行值相同的问题及原因解析
窗口函数累计和的框架问题解答
问题详情
数据表结构
Candidates表:
| employee_id | experience | salary |
|---|---|---|
| 1 | Junior | 10000 |
| 9 | Junior | 15000 |
| 2 | Senior | 20000 |
| 11 | Senior | 16000 |
| 13 | Senior | 50000 |
| 4 | Junior | 40000 |
需求
按experience和salary排序,计算salary的逐行累计和。
初始代码
SELECT employee_id, experience, salary, SUM(salary) OVER(ORDER BY experience, salary) as cumsum FROM Candidates
期望输出
| employee_id | experience | salary | cumsum |
|---|---|---|---|
| 11 | Senior | 16000 | 16000 |
| 2 | Senior | 20000 | 36000 |
| 13 | Senior | 50000 | 86000 |
| 1 | Junior | 10000 | 96000 |
| 9 | Junior | 15000 | 111000 |
| 4 | Junior | 40000 | 151000 |
实际输出
| employee_id | experience | salary | cumsum |
|---|---|---|---|
| 11 | Senior | 16000 | 16000 |
| 2 | Senior | 20000 | 36000 |
| 13 | Senior | 50000 | 151000 |
| 1 | Junior | 10000 | 151000 |
| 9 | Junior | 15000 | 151000 |
| 4 | Junior | 40000 | 151000 |
补充说明
salary字段的值是唯一的。
修正后代码
添加ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW后代码正常运行:
SELECT employee_id, experience, salary, SUM(salary) OVER(ORDER BY experience, salary ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as cumsum FROM Candidates
原因解释
问题出在窗口函数的默认框架规则上:
- 当仅在
OVER()子句中指定ORDER BY而不声明窗口框架时,多数SQL数据库会默认使用RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW作为窗口范围。 RANGE框架是基于ORDER BY列的逻辑值范围确定窗口:它会包含所有ORDER BY列组合值小于等于当前行值的行。但你的场景中,数据库对experience字符串的排序逻辑出现了预期外判断,导致RANGE框架错误地将所有行纳入当前窗口,直接计算了所有salary的总和。- 而
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW是基于物理行位置的框架:它明确指定窗口包含排序后结果集的第一行到当前行的所有行,完全按行顺序逐行累计,不受ORDER BY列的逻辑值判断影响,因此能得到预期的逐行累计结果。
内容的提问来源于stack exchange,提问作者MTWinner
相关产品推荐
相关产品推荐

