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

相关推荐
曹牧6 小时前
Oracle EXPLAIN PLAN
数据库·oracle
贤时间6 小时前
codex 助力oracle ebs 开发
数据库·oracle
秉承初心7 小时前
PostgreSQL 数据性能瓶颈突破实战
数据库·postgresql·oracle
Curvatureflight10 小时前
MySQL 深分页越来越慢?从 LIMIT OFFSET 改成游标分页
数据库·oracle
XZ-07000110 小时前
MySQL事务
数据库·mysql·oracle
tiancaijiben10 小时前
阿里云函数计算FC如何实现网站的定时任务与自动化
数据库·oracle·dba
xfhuangfu10 小时前
Oracle 19c 多租户体系架构介绍
数据库·oracle·架构
杨云龙UP13 小时前
Spotlight 接入 Oracle 数据库监控操作指南 2026-06-16
数据库·oracle·性能监控·预警·阈值·spotlight·瓶颈分析
unique13 小时前
AI Coding 采集方案探索
jvm·人工智能·oracle
wangbing112513 小时前
Oracle的撤回功能
数据库·oracle