(课堂笔记)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

相关推荐
jnrjian2 小时前
Oracle 权限 role 权限 下次登录生效或者set role, sys permission 立即生效
数据库·oracle
叶小鸡2 小时前
Java 篇-项目实战-天机学堂(从0到1)-day8
数据库·oracle
Irene19913 小时前
(课堂笔记)Oracle 聚合函数与 GROUP BY 分组查询
oracle·聚合函数
阿坤带你走近大数据3 小时前
Oracle报错-锁问题
数据库·oracle
木易 士心4 小时前
云数据库 Clouder 认证:SQL 基础开发与应用题型分析
数据库·后端·sql·oracle
Princesk4 小时前
DBA之路--oracle数据隐型转换
数据库·oracle·dba
zxrhhm4 小时前
Oracle 19c RAC 默认表空间类型的管理及总结
数据库·oracle
gbase_lmax5 小时前
gbase8s数据库权限分类及基础使用
数据库·oracle