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 就没有顺序,只能返回固定总和。

相关推荐
早日退休!!!7 小时前
《数据结构选型指南》笔记
数据结构·数据库·oracle
阿坤带你走近大数据10 小时前
怎么查看当前oracle库下的表空间temp大小或者默认大小
数据库·oracle
hljqfl15 小时前
Oracle存储结构
数据库·oracle
m0_7162550016 小时前
批处理一道例题+答案解析+批处理知识点总结 | 批处理高频易错场景 + 正确写法对照表
数据库·oracle
俺不要写代码16 小时前
数据库:DML
数据库·oracle
Irene199117 小时前
Oracle 中:为什么 from 子查询后面需要一个别名
数据库·oracle
杨云龙UP18 小时前
Oracle数据库启动失败:ORA-29701、ORA-01565、ORA-17503故障处理记录_20260429
linux·运维·数据库·oracle·centos
懒铭心19 小时前
Oracle 19c 备份与恢复
oracle
于歌85219 小时前
Oracle批处理操作方法
数据库·oracle