(课堂笔记)Oracle 窗口函数/开窗函数/分析函数

窗口函数(OVER)可在SELECT中实现分组计算而不改变行数,核心包括:

1)排名函数(ROW_NUMBER/RANK/DENSE_RANK)用于TOPN查询;

2)LAG/LEAD获取相邻行数据;

3)FIRST_VALUE取组内首条记录;

4)NTILE数据分桶;

5)聚合开窗(SUM/AVG)实现累计计算。


与聚合函数不同,窗口函数保留原行数,通过PARTITION BY分组、ORDER BY排序。


典型应用包括部门薪资排名、同环比分析、累计求和等,需注意结果需子查询过滤,且必须明确排序规则。


(课堂笔记)窗口函数/开窗函数/分析函数


补充:窗口函数或开窗函数,为什么会叫窗口或开窗,怎么理解的


窗口函数为什么叫"窗口"?------ 概念理解

以下是根据你提供的课堂内容,整理出的**《窗口函数(开窗函数/分析函数)完整复习笔记》**,结构清晰、便于学生复习和记忆:


📌 一、窗口函数概述

基本语法:

sql

复制代码
函数() OVER (PARTITION BY 分组字段 ORDER BY 排序字段)

核心特点:

特点 说明
✅ 只能写在 SELECT 后面 不能用在 WHERE/HAVING 中
✅ 不改变行数 每行返回一个值,不会合并多行
✅ 可分组+排序 通过 PARTITION BY 分组,ORDER BY 排序

PARTITION 可以省略


PARTITION by 1 order by 1 等于默认值
📌 窗口函数 vs 聚合函数

  • 聚合函数:多行 → 1 行

  • 窗口函数:多行 → 多行(每行带聚合结果)


📌 二、排名函数(取 TOP N)

1. 三种排名函数对比

函数 特点 并列场景示例
ROW_NUMBER() 连续不重复 1,2,3,4
RANK() 重复有间隔 1,1,3,4
DENSE_RANK() 重复无间隔 1,1,2,3

RANK 排名 DENSE 稠密,密集


RANK() 并列跳号 1,2,2,4

DENSE_RANK() 并列不跳 1,2,2,3

2. 基础语法

sql

复制代码
ROW_NUMBER() OVER (PARTITION BY 分组字段 ORDER BY 排序字段)

3. 实战示例

✅ 每个部门按薪资排名

sql

复制代码
SELECT 
  ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS RN,
  E.*
FROM EMP E;

注意:E.*可以用,直接使用*不行,因为*包括所有,后面不能再加其他字段。


✅ 每个部门薪资前三名

sql

复制代码
SELECT * FROM (
  SELECT 
    ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS RN,
    E.*
  FROM EMP E
) WHERE RN <= 3;
✅ 公司薪资第 6 名

sql

复制代码
SELECT * FROM (
  SELECT 
    ROW_NUMBER() OVER (ORDER BY SAL DESC) AS RN,
    E.*
  FROM EMP E
) WHERE RN = 6;
✅ 每年入职员工月薪前三名

sql

复制代码
SELECT * FROM (
  SELECT 
    ROW_NUMBER() OVER (
      PARTITION BY TO_CHAR(HIREDATE,'YYYY') 
      ORDER BY SAL + NVL(COMM,0) DESC
    ) AS RN,
    E.*
  FROM EMP E
) WHERE RN <= 3;

📌 三、LAG / LEAD(取上下行,计算同环比)

1. 语法

sql

复制代码
LAG(字段, 偏移量, 缺省值) OVER (PARTITION BY 分组 ORDER BY 排序)
LEAD(字段, 偏移量, 缺省值) OVER (PARTITION BY 分组 ORDER BY 排序)
函数 说明
LAG 取上一行(往上偏移)
LEAD 取下一行(往下偏移)

✅ 偏移量默认 = 1,缺省值默认 = NULL


空值不会触发缺省值,会保持为空。

函数 单词 中文含义 词性
LAG lag 滞后、延迟、落后 动词/名词
LEAD lead 领先、超前、引导 动词/名词

2. 实战示例

✅ 与上一个入职员工薪资差

sql

复制代码
SELECT 
  E.*,
  ABS(SAL - LAG(SAL, 1, 8888) OVER (ORDER BY HIREDATE)) AS 薪资差
FROM EMP E;
✅ 每个部门中,与下一个入职员工薪资差

sql

复制代码
SELECT 
  E.*,
  LEAD(SAL) OVER (PARTITION BY DEPTNO ORDER BY HIREDATE) AS NEXT_SAL
FROM EMP E;

📌 四、FIRST_VALUE(取每组第一个值)

1. 语法

sql

复制代码
FIRST_VALUE(目标字段) OVER (PARTITION BY 分组 ORDER BY 排序)

✅ 取每组排序后的第一条数据的指定字段值

2. 实战示例

✅ 每个部门薪资最高的员工姓名

sql

复制代码
SELECT DISTINCT
  FIRST_VALUE(ENAME) OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS 最高薪资员工,
  DEPTNO
FROM EMP;

💡 配合 ROW_NUMBER 方式(更灵活):

sql

复制代码
SELECT DEPTNO, ENAME FROM (
  SELECT 
    DEPTNO, ENAME,
    ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS RN
  FROM EMP
) WHERE RN = 1;

📌 五、NTILE(数据切片 / 分桶)


1. 语法

sql

复制代码
NTILE(桶数) OVER (PARTITION BY 分组 ORDER BY 排序)

✅ 将数据分成 N 份,每行标记所属桶号(1 ~ N)


NTILE 整体读作:/ˈen.taɪl/(恩-太欧)

(重音在第一个音节 "en" 上)


在专业交流中(如讨论数据分桶),通常直接读 "N-tile"(强调 N 的数字,如 "4-tile" 表示四分位)即可,对方更容易理解。


2. 实战示例

✅ 每个部门薪资分成 3 档(高/中/低),输出高薪员工

sql

sql 复制代码
SELECT * FROM (
  SELECT 
    NTILE(3) OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS NT,
    E.*
  FROM EMP E
) WHERE NT = 1;

切片规则

余数分配规律图
sql 复制代码
余数决定前几个桶多1行:

余数=0:  ████ ████ ████ ████  (全部平均)
余数=1:  █████ ████ ████ ████  (桶1多1)
余数=2:  █████ █████ ████ ████  (桶1,桶2多1)
余数=3:  █████ █████ █████ ████  (桶1,2,3多1)

图例:████ = 基数行  █ = 多出的1行
快速判断流程图

第一步:计算桶大小

第二步:数据排序

第三步:按桶大小,依次填充,一个填满了才开始下一个桶填充。

sql 复制代码
开始
  │
  ▼
┌─────────────────┐
│ 总行数 ÷ 桶数    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐      ┌─────────────────┐
│ 有 余数 吗?    │────NO─→│ 所有桶平均分配  │
└────────┬────────┘      └─────────────────┘
         │YES
         ▼
┌─────────────────┐
│ 前(余数)个桶    │
│ 各多分1行       │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 从第1行开始     │
│ 按桶容量依次填充 │
└─────────────────┘
正确理解:

是"尽可能平均",不是"绝对平均"

多余的行给编号小的桶

和排序后的位置强相关


记忆口诀

NTILE 分桶按顺序,先排好队再分配
除不尽的余数行,前几个桶多一个位


📌 六、聚合开窗函数(累计 / 占比)

1. 语法

sql

复制代码
SUM() OVER (PARTITION BY 分组 ORDER BY 排序)
AVG() OVER (PARTITION BY 分组 ORDER BY 排序)

✅ 带 ORDER BY:累计值(逐行累加)

✅ 不带 ORDER BY:全组聚合值(每行相同)


关联阅读推荐

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

2. 实战示例

✅ 公司累计人力成本(随时间推移)

sql

复制代码
SELECT 
  E.*,
  SUM(SAL) OVER (ORDER BY HIREDATE) AS 累计薪资
FROM EMP E;
✅ 每个部门累计人力成本

sql

复制代码
SELECT 
  E.*,
  SUM(SAL) OVER (PARTITION BY DEPTNO ORDER BY HIREDATE) AS 部门累计薪资
FROM EMP E;
✅ 员工薪资占部门总薪资比例

sql

复制代码
SELECT 
  E.*,
  SAL / SUM(SAL) OVER (PARTITION BY DEPTNO) AS 部门薪资占比
FROM EMP E;
✅ 员工薪资占公司总成本比例

sql

复制代码
SELECT 
  E.*,
  SAL / SUM(SAL) OVER () AS 公司薪资占比
FROM EMP E;
✅ 每个部门累计平均薪资

sql

复制代码
SELECT 
  E.*,
  AVG(SAL) OVER (PARTITION BY DEPTNO ORDER BY HIREDATE) AS 累计平均薪资
FROM EMP E;

📌 七、窗口范围控制(了解)


最近几行(当前行的之前或之后几行)


sql

复制代码
SUM(SAL) OVER (
  PARTITION BY DEPTNO 
  ORDER BY HIREDATE
  ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
)
关键字 含义
PRECEDING 之前
FOLLOWING 之后
CURRENT ROW 当前行

💡 一般笔试/面试较少深入,了解即可。


📌 八、常见面试题

Q1:用过哪些窗口函数?

场景 函数
TOP N 排名 ROW_NUMBER / RANK / DENSE_RANK
同环比 LAG / LEAD
数据切片 NTILE
累计金额/占比 SUM / AVG OVER

Q2:窗口函数和聚合函数的区别?

对比点 聚合函数 窗口函数
输出行数 多行 → 1 行 多行 → 多行
分组方式 GROUP BY PARTITION BY
使用位置 SELECT / HAVING 仅 SELECT
能否累计 ✅(带 ORDER BY)

Oracle 聚合函数 vs 窗口函数 对比总结(书写顺序与执行顺序示例)


📌 九、课堂练习(自测用)

练习1:每个办公地点薪资前三名

sql

复制代码
SELECT LOC, ENAME, SAL FROM (
  SELECT 
    LOC, ENAME, SAL,
    ROW_NUMBER() OVER (PARTITION BY LOC ORDER BY SAL DESC) AS RN
  FROM EMP E
  JOIN DEPT D ON E.DEPTNO = D.DEPTNO
) WHERE RN <= 3;

练习2:每个岗位上一个入职员工的薪资

sql

复制代码
SELECT 
  JOB, HIREDATE, SAL,
  LAG(SAL) OVER (PARTITION BY JOB ORDER BY HIREDATE) AS 上一个薪资
FROM EMP;

练习3:每年薪资最高的员工姓名

sql

复制代码
SELECT DISTINCT
  TO_CHAR(HIREDATE,'YYYY') AS YEAR,
  FIRST_VALUE(ENAME) OVER (
    PARTITION BY TO_CHAR(HIREDATE,'YYYY') 
    ORDER BY SAL DESC
  ) AS ENAME
FROM EMP;

📌 十、易错提醒

❌ 错误写法 ✅ 正确理解
WHERE RN <= 3 直接使用别名 窗口函数的结果需要子查询
PARTITION BY 后加 WHERE 分区是分组,不是过滤
忘记 ORDER BY 导致排名不准确 排名/累计必须明确排序规则
把 LAG 当 LEAD 用 LAG = 向上取,LEAD = 向下取

📊 SQL 窗口函数速查表(一页全)

🔧 基础语法

sql

复制代码
函数() OVER (PARTITION BY 分组字段 ORDER BY 排序字段)

📌 排名函数(TOP N)

函数 效果 并列示例
ROW_NUMBER() 连续不重复 1,2,3,4
RANK() 有间隔 1,1,3,4
DENSE_RANK() 无间隔 1,1,2,3

sql

复制代码
-- 每个部门薪资前三名
SELECT * FROM (
  SELECT ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS RN, E.*
  FROM EMP E
) WHERE RN <= 3;

📌 LAG / LEAD(上下行)

函数 说明
LAG(字段,偏移,缺省) 取上一行
LEAD(字段,偏移,缺省) 取下一行

sql

复制代码
-- 与上一个入职员工薪资差
SELECT SAL - LAG(SAL) OVER (ORDER BY HIREDATE) FROM EMP;

📌 FIRST_VALUE(每组首个)

sql

复制代码
-- 每个部门薪资最高的员工
SELECT DISTINCT
  FIRST_VALUE(ENAME) OVER (PARTITION BY DEPTNO ORDER BY SAL DESC)
FROM EMP;

📌 NTILE(数据切片)

sql

复制代码
-- 每个部门薪资分3档,取高薪档
SELECT * FROM (
  SELECT NTILE(3) OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS NT, E.*
  FROM EMP
) WHERE NT = 1;

📌 聚合开窗(累计/占比)

写法 效果
SUM() OVER(PARTITION BY 组) 每组总和(每行相同)
SUM() OVER(ORDER BY 日期) 累计求和
SUM() OVER(PARTITION BY 组 ORDER BY 日期) 组内累计

sql

复制代码
-- 部门薪资占比
SELECT SAL / SUM(SAL) OVER (PARTITION BY DEPTNO) FROM EMP;

-- 累计人力成本
SELECT SUM(SAL) OVER (ORDER BY HIREDATE) FROM EMP;

⚡ 执行顺序

text

复制代码
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
                    ↑
            窗口函数在这里计算

⚠️ 核心要点

✅ 可以 ❌ 不可以
写在 SELECT 后 用在 WHERE/HAVING
PARTITION BY 分组 直接使用别名过滤
每行返回一个值 合并多行

🎯 场景速查

需求 用什么
排名 / TOP N ROW_NUMBER
同环比 LAG / LEAD
每组第一名 FIRST_VALUE
分档/分桶 NTILE
累计求和 SUM OVER(ORDER BY)
占比 SUM OVER(PARTITION BY)

💡 记忆口诀:排名用ROW,上下用LAG,切片NTILE,累计SUM OVER

相关推荐
ClouGence7 天前
Oracle 数据同步为什么会出现数据不一致?长事务是常被忽略的原因
数据库·后端·oracle
ClouGence13 天前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
曹牧13 天前
Oracle EXPLAIN PLAN
数据库·oracle
贤时间13 天前
codex 助力oracle ebs 开发
数据库·oracle
秉承初心13 天前
PostgreSQL 数据性能瓶颈突破实战
数据库·postgresql·oracle
Curvatureflight14 天前
MySQL 深分页越来越慢?从 LIMIT OFFSET 改成游标分页
数据库·oracle
XZ-07000114 天前
MySQL事务
数据库·mysql·oracle
tiancaijiben14 天前
阿里云函数计算FC如何实现网站的定时任务与自动化
数据库·oracle·dba
xfhuangfu14 天前
Oracle 19c 多租户体系架构介绍
数据库·oracle·架构
杨云龙UP14 天前
Spotlight 接入 Oracle 数据库监控操作指南 2026-06-16
数据库·oracle·性能监控·预警·阈值·spotlight·瓶颈分析