当前位置: 首页 > news >正文

SQL 窗口函数 学习笔记

易错点

查找每个大陆(continent)中最大的国家(按区域area),显示该大洲 (continent),国家名(name)和面积(area) 代码能不能这样写

SELECT continent, name, area, rank() over(partition by continent order by area desc) as rank FROM world WHERE rank = 1

不可以,窗口函数别名无法直接在 WHERE 中使用。MySQL 中,WHERE子句的执行顺序早于窗口函数,因此无法直接引用窗口函数的别名(如rank = 1),会报 “未知列 rank” 错误

用子查询

SELECT continent, name, area FROM ( SELECT continent, name, area, RANK() OVER(PARTITION BY continent ORDER BY area DESC) AS rnk FROM world WHERE area > 0 ) AS biao WHERE rnk = 1

一、窗口函数核心概念

1. 定义

窗口函数(Window Function)是 SQL 中用于在不合并行的前提下,对指定范围(窗口)内的数据进行聚合、排序、分析的函数。

  • 普通聚合函数(GROUP BY + SUM/COUNT):将多行合并为一行返回结果;
  • 窗口函数:为每行数据返回一个计算结果,保留原表所有行。

2. 通俗比喻

普通聚合函数:把全班按性别分组,只返回每组的总分(每组 1 行);窗口函数:给每个同学的成绩单上,额外标注其性别组的总分(每行都有)。

3. 核心语法

函数名(参数) OVER ( PARTITION BY 分区列 ORDER BY 排序列 ASC/DESC ROWS/RANGE BETWEEN 起始边界 AND 结束边界 )

说明

  • PARTITION BY 分区列ORDER BY 排序列ROWS/RANGE均为可选子句,根据业务需求选择是否添加;
  • 实际使用时直接替换 “分区列”“排序列” 等为具体字段名即可,比如PARTITION BY 班级ORDER BY 分数 DESC

二、窗口函数分类及详解

1. 聚合类窗口函数

(1)核心函数

SUM ()、COUNT ()、AVG ()、MAX ()、MIN ()(基于普通聚合函数改造)。

(2)关键特点
  • 无 ORDER BY:计算整个分区的聚合值;
  • 有 ORDER BY:计算 “分区首行到当前行” 的累计值。
(3)实战案例

基础表(score)

学号姓名科目分数班级
1张三数学90一班
2李四数学85一班
3王五数学95二班
4赵六语文88一班

案例 1:计算班级数学平均分(无 ORDER BY)

SELECT 学号, 姓名, 分数, 班级, -- 按班级分区,计算整个分区的平均分 AVG(分数) OVER (PARTITION BY 班级) AS 班级数学平均分 FROM score WHERE 科目='数学';
学号姓名分数班级班级数学平均分
1张三90一班87.5
2李四85一班87.5
3王五95二班95

案例 2:计算班级分数累计值(有 ORDER BY)

SELECT 学号, 姓名, 分数, 班级, SUM(分数) OVER (PARTITION BY 班级 ORDER BY 分数) AS 班级累计分数 FROM score;

结果(一班)

学号姓名分数班级班级累计分数
2李四85一班85
1张三90一班175
4赵六88一班263

2. 排序类窗口函数

(1)核心函数及区别
函数名核心特点示例(同分数 90)
ROW_NUMBER()无重复排名,即使分数相同也编唯一号1,2,3,4
RANK()有重复排名,重复值占同一位置,后续排名跳空1,2,2,4
DENSE_RANK()有重复排名,重复值占同一位置,后续排名不跳空1,2,2,3
(2)高频疑问点
  • ❓ 窗口内的 ORDER BY 是否改变输出行顺序?❌ 不改变!窗口内的 ORDER BY 仅用于计算排名的依据,若需输出结果排序,需加全局 ORDER BY
  • ❓ RANK () 必须搭配 ORDER BY 吗?语法上可选,但业务上必须!省略 ORDER BY 时,分区内所有行排名都是 1,无业务意义。
SELECT 姓名, 班级, 分数, ROW_NUMBER() OVER (PARTITION BY 班级 ORDER BY 分数 DESC) AS 行号排名, RANK() OVER (PARTITION BY 班级 ORDER BY 分数 DESC) AS 普通排名, DENSE_RANK() OVER (PARTITION BY 班级 ORDER BY 分数 DESC) AS 密集排名 FROM score;

结果(一班,新增王五分数 90)

姓名班级分数行号排名普通排名密集排名
张三一班90111
王五一班90211
李四一班85332

3. 分析类窗口函数

(1)核心函数及作用
函数作用默认偏移量
LAG (列名,n, 默认值)获取当前行的前 n 行数据1
LEAD (列名,n, 默认值)获取当前行的后 n 行数据1
FIRST_VALUE (列名)获取分区内排序后的第一个值-
LAST_VALUE (列名)获取分区内排序后的最后一个值-
(2)高频疑问点
  • ❓ LAST_VALUE () 为什么返回值不符合预期?默认窗口范围是 “分区首行到当前行”,需手动扩展到整个分区:ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  • ❓ 分析类函数需要加 ORDER BY 吗?必须加!“前 / 后行”“首 / 末值” 依赖排序规则,省略则逻辑混乱。
(3)实战案例(销售表 sales)
日期区域销售额
2024-01-01华东1000
2024-01-02华东1200
2024-01-03华东1100

案例 1:LAG () 计算环比增长率

SELECT 日期, 区域, 销售额, LAG(销售额, 1, 0) OVER (PARTITION BY 区域 ORDER BY 日期) AS 前一天销售额, ROUND((销售额 - LAG(销售额,1,0) OVER (PARTITION BY 区域 ORDER BY 日期)) / NULLIF(LAG(销售额,1,0) OVER (PARTITION BY 区域 ORDER BY 日期),0)*100,2) AS 环比增长率(%) FROM sales;

结果

日期区域销售额前一天销售额环比增长率 (%)
2024-01-01华东10000NULL
2024-01-02华东1200100020.00
2024-01-03华东11001200-8.33

案例 2:LAST_VALUE () 正确获取分区末值

SELECT 日期, 区域, 销售额, LAST_VALUE(销售额) OVER ( PARTITION BY 区域 ORDER BY 日期 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS 区域最后一天销售额 FROM sales;

结果

日期区域销售额区域最后一天销售额
2024-01-01华东10001100
2024-01-02华东12001100
2024-01-03华东11001100

4. 窗口范围(ROWS/RANGE)

(1)核心概念

窗口范围定义 “分区 + 排序后,哪些行参与当前行的计算”,是窗口函数的 “精细控制层”。

(2)核心语法(可直接复用)
ROWS BETWEEN 起始边界 AND 结束边界 -- 或 RANGE BETWEEN 起始边界 AND 结束边界

常用边界值(直接写即可)

边界值含义
UNBOUNDED PRECEDING分区第一行
CURRENT ROW当前行
UNBOUNDED FOLLOWING分区最后一行
n PRECEDING当前行的前 n 行(n 写具体数字,如 1 PRECEDING)
n FOLLOWING当前行的后 n 行(n 写具体数字,如 1 FOLLOWING)
(3)核心区别(ROWS vs RANGE)
类型定义方式特点示例(分数 90 重复)
ROWS按物理行数限定直接数行,逻辑简单累计到第 3 行 = 80+85+90
RANGE按列值大小限定相同值的行全部包含累计到 90=80+85+90+90
(4)默认规则(必记)
场景默认窗口范围
有 ORDER BY,无显式范围RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
无 ORDER BY,无显式范围ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
(5)实战案例:3 日移动平均
SELECT 日期, 销售额, AVG(销售额) OVER ( ORDER BY 日期 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS 3日移动平均 FROM sales;

结果

日期销售额3 日移动平均
2024-01-0110001000.00
2024-01-0212001100.00
2024-01-0311001100.00

三、高频易错点汇总

  1. 窗口内的 ORDER BY ≠ 全局 ORDER BY:前者仅用于计算规则,后者控制输出行顺序;
  2. RANK () 省略 ORDER BY 无业务意义,所有行排名为 1;
  3. LAST_VALUE () 需手动扩展窗口范围到整个分区;
  4. 分析类函数(LAG/LEAD/FIRST_VALUE/LAST_VALUE)必须加 ORDER BY;
  5. OVER () 内的 WHERE 是非标准写法,数据筛选优先用全局 WHERE 子句;
  6. 除数为 0 问题:用 NULLIF (值,0) 避免环比增长率报错。

四、常见业务场景

函数类型典型场景示例需求
聚合类累计计算、分组平均值累计销售额、班级平均分
排序类排名、取 Top N班级成绩排名、各区域销售额 Top3
分析类环比 / 同比、首尾值对比日销售额环比、本月首日销售额对比
窗口范围移动平均、相邻行分析7 日销量平均、前后 1 天数据对比

五、核心总结

  1. 窗口函数核心:不合并行,为每行返回基于 “分区 + 排序 + 范围” 的计算结果;
  2. 三大分类:聚合类(累计 / 分组计算)、排序类(排名)、分析类(跨行对比);
  3. 窗口范围:ROWS 按物理行(常用),RANGE 按值范围(处理重复值);
  4. 关键技巧:LAST_VALUE () 扩展窗口范围、排序类函数必加 ORDER BY、全局 ORDER BY 控制输出顺序。
http://www.jsqmd.com/news/477975/

相关文章:

  • R提供了一些函数用于判断逻辑表达式的结果
  • C++面试总结(1)
  • MacBook + 台式机如何共享双屏?KVM切换器是终极答案|附TESmart全系对比推荐
  • 深入解析桥接模式:一个C++模板实现的通用桥接类库
  • 统信UOS 20 高效部署实战指南
  • 机械臂轨迹规划是机器人开发中的重头戏,今天咱们用Matlab的Robotics Toolbox带PUMA560走两步。先给机械臂充个电——初始化模型
  • 报错v-bind is missing expression
  • 局部遮阴光伏MPPT仿真:粒子群算法详解及视频解析
  • Multisim仿真TL494BUCK闭环,稳定输出5v,带软启动。 电流限制为0.14A电流...
  • 从时序建模到寿命预测:TCN在轴承RUL预测中的实战解析
  • k8s工作负载-HPA控制器
  • 【Docker】Linux系统上卸载旧Docker、卸载Podman并重新安装Docker及配置国内镜像源
  • 基于二次规划的路径规划与速度规划:从MATLAB到C++的实践
  • 又崩了?C++的灵活,新手的坑,老手的泪,高手都没控制就泄漏了
  • 2026做媒介宣发,真没必要再求人找关系了
  • LTspice仿真学习指南:掌握LDO模拟集成电路电源及其关键仿真技巧(包括相位裕度等)
  • 周期 Pattern Removal 算法
  • OpenClaw 技能插件开发实战:适配职业教育的 AI 实训案例
  • 10kW虚拟同步发电机(VSG)小信号稳定控制matlab仿真 【985双一流专业的电气工程博...
  • 调速器响应,0.05秒级延迟
  • 我收藏的一个非常详细的CTF挑战赛题库,建议收藏!
  • 麻雀算法的逆袭:RSSA实战解析
  • 傅里叶变换
  • 光伏MPPT电导增量法仿真模型及配套视频
  • GEE平台下Landsat时序RSEI计算与生态演变分析
  • 队列的实现与应用详解
  • 一、CentOS安装Mysql
  • VSCode 配置 IAR 工程编译、下载与调试指南
  • Matlab语音信号去噪GUI:实现正弦噪声与高斯噪声的滤波处理,巴特沃斯低通与小波变换去噪功能
  • NVMe1.4 Admin Command解析:Format与Identify的LBA格式与安全擦除机制