Oracle:为什么 ORDER BY 能让 SUM() 变成累计?

SQL窗口函数中ORDER BY对SUM()的影响解析


在SQL窗口函数中,ORDER BY子句会改变SUM()的行为。


没有ORDER BY时,SUM()计算整个分组的固定总和(如部门总薪资8750);


添加ORDER BY后,SUM()变为按指定顺序的累计求和(如2450→7450→8750)。


这种差异源于窗口范围的改变:无ORDER BY时窗口是整个分组,有ORDER BY时窗口默认从第一行到当前行。


其他聚合函数如AVG、COUNT等也遵循相同逻辑。


该设计体现了SQL对数据顺序处理的灵活性,使分析函数能同时支持整体统计和滚动计算两种模式。


为什么 ORDER BY 能让 SUM() 变成累计?

一、核心原理

sql

复制代码
-- 没有 ORDER BY:返回分组总和
SUM(SAL) OVER (PARTITION BY DEPTNO)
-- 结果:每个部门所有行的值都一样(部门总薪资)

-- 有 ORDER BY:返回累计值
SUM(SAL) OVER (PARTITION BY DEPTNO ORDER BY HIREDATE)
-- 结果:从第一行到当前行的累加

二、底层执行逻辑对比

场景:10号部门员工入职情况

员工 入职日期 薪资
CLARK 1981-06-09 2450
KING 1981-11-17 5000
MILLER 1982-01-23 1300

❌ 没有 ORDER BY(分组总和)

sql

复制代码
SUM(SAL) OVER (PARTITION BY DEPTNO)

执行过程:

text

复制代码
步骤1:扫描整个10号部门,计算总薪资 = 2450 + 5000 + 1300 = 8750
步骤2:把 8750 填入每一行

结果:

员工 薪资 SUM()
CLARK 2450 8750
KING 5000 8750
MILLER 1300 8750

相当于:先算完总和,再广播到每一行


✅ 有 ORDER BY(累计求和)

sql

复制代码
SUM(SAL) OVER (PARTITION BY DEPTNO ORDER BY HIREDATE)

执行过程:

text

复制代码
步骤1:按 HIREDATE 排序 → CLARK(6月) → KING(11月) → MILLER(1月)
步骤2:逐行计算累加
  - 第1行(CLARK):累加 = 2450
  - 第2行(KING):累加 = 2450 + 5000 = 7450
  - 第3行(MILLER):累加 = 2450 + 5000 + 1300 = 8750

结果:

员工 入职日期 薪资 SUM()
CLARK 1981-06-09 2450 2450
KING 1981-11-17 5000 7450
MILLER 1982-01-23 1300 8750

相当于:按顺序维护一个"运行总数"


三、图解对比

text

复制代码
没有 ORDER BY(分组总和):
┌─────────────────────────────────────┐
│  计算总薪资 = 8750                    │
│  ↓                                  │
│  ┌────────┐                         │
│  │  CLARK │ ← 8750                   │
│  │  KING  │ ← 8750                   │
│  │ MILLER │ ← 8750                   │
│  └────────┘                         │
└─────────────────────────────────────┘

有 ORDER BY(累计求和):
┌─────────────────────────────────────┐
│  按日期排序后逐行累加:               │
│  ┌────────┐                         │
│  │  CLARK │ ← 2450  (只有自己)       │
│  │  KING  │ ← 2450+5000=7450        │
│  │ MILLER │ ← 2450+5000+1300=8750   │
│  └────────┘                         │
└─────────────────────────────────────┘

四、所有聚合窗口函数的行为

函数 无 ORDER BY 有 ORDER BY
SUM() 分组总和 累计求和
AVG() 分组平均 累计平均
COUNT() 分组总数 累计计数
MAX() 分组最大值 累计最大值(到当前行为止的最大值)
MIN() 分组最小值 累计最小值(到当前行为止的最小值)

示例:累计最大值

sql

复制代码
SELECT 
    ENAME,
    HIREDATE,
    SAL,
    MAX(SAL) OVER (ORDER BY HIREDATE) AS 截止当前的最高薪资
FROM EMP;

结果:

姓名 入职日期 薪资 截止当前最高
SMITH 1980-12-17 800 800
JONES 1981-04-02 2975 2975
BLAKE 1981-05-01 2850 2975
KING 1981-11-17 5000 5000

五、为什么这样设计?

设计哲学

特性 说明
无 ORDER BY 表示对整个"窗口"(分组)做聚合,不关心顺序
有 ORDER BY 给窗口赋予了"顺序"概念,聚合变成"滚动计算"

类比 Excel

text

复制代码
无 ORDER BY = 选中整列,看总和
有 ORDER BY = 从第一行开始,每行显示到当前行的累计

六、完整语法理解

sql

复制代码
SUM(SAL) OVER (
    PARTITION BY DEPTNO   -- 1. 定义窗口边界(按部门分组)
    ORDER BY HIREDATE     -- 2. 定义窗口内顺序(按日期排序)
)
-- 默认窗口范围:ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-- 即:从分组第一行 → 当前行

等价完整写法

sql

复制代码
SUM(SAL) OVER (
    PARTITION BY DEPTNO 
    ORDER BY HIREDATE
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 默认行为
)

七、记忆口诀

没有排序,全员一样
有了排序,累加递进
日期顺序,决定累加
从前往后,逐行滚动


八、快速自测

sql

复制代码
-- 问:下面两句话的区别是什么?
-- Q1: SUM(SAL) OVER (PARTITION BY DEPTNO)
-- Q2: SUM(SAL) OVER (PARTITION BY DEPTNO ORDER BY HIREDATE)

-- 答:
-- Q1:返回每个部门的薪资总和(每行相同)
-- Q2:返回按入职时间累计的薪资(逐行递增)

💡 总结一句话

ORDER BY 告诉数据库"按照什么顺序累加",没有 ORDER BY 就没有顺序,只能返回固定总和。

相关推荐
天天进步一点点2 小时前
QSqlQuery删除sqlite数据库执行drop数据表报错database table is locked unable to fetch row问题
数据库·oracle·sqlite
Gauss松鼠会6 小时前
Prometheus 实现 openGauss 的指标监控(二)Prometheus 中添加 openGauss指标
网络·数据库·oracle·prometheus·opengauss·经验总结
煎饼学大模型19 小时前
Agent 的“大脑-手“解耦架构:当推理层和工具执行层各自独立演进
数据库·人工智能·oracle·架构·agent
暖和_白开水1 天前
数据分析agent(十三_2):业务层和持久层相互转换mapper
数据库·oracle·数据分析
曹牧1 天前
Oracle:参数化“常量”
数据库·oracle
一十九的酒1 天前
Oracle 密码恢复实战:使用 SPARE4 哈希值还原用户密码
数据库·oracle·密码丢失·密码恢复·账户锁定
许彰午2 天前
05-十分钟读懂AWR报告
oracle
whn19772 天前
oracle的节点2无法启动asm实例 提示PMON terminating the instance due to error 481
数据库·oracle·wpf
沉默de荒年 对月影成2 天前
机房断电搞崩服务器 | 人大金仓 V8 全量备份跨实例完整恢复实录
运维·服务器·oracle