MySQL 窗口函数详解

很多人第一次看到窗口函数的时候,都会觉得:

sql 复制代码
这是什么东西?

尤其是:

sql 复制代码
OVER()
ROW_NUMBER()
RANK()

看起来特别抽象。

但实际上:

窗口函数本质上就是"在保留原数据的基础上做统计"。

这是它和:

sql 复制代码
GROUP BY

最大的区别。


一、先理解什么是窗口函数

先看一个普通需求。

比如有一张成绩表:

id name score
1 张三 90
2 李四 85
3 王五 95

现在需求:

查询:

  • 学生姓名
  • 分数
  • 排名

这时候:

sql 复制代码
GROUP BY

其实做不了。

因为:

sql 复制代码
GROUP BY

会把原数据"压缩"。

而窗口函数:

可以一边保留原数据,一边做统计。


二、窗口函数基本语法

sql 复制代码
函数名() OVER()

比如:

sql 复制代码
SELECT
    name,
    score,
    ROW_NUMBER() OVER()
FROM student;

三、OVER() 到底是什么

这个是窗口函数的核心。

你可以把它理解成:

"规定统计范围"

比如:

sql 复制代码
OVER()

表示:

sql 复制代码
对整个结果集进行处理

四、第一个窗口函数:ROW_NUMBER()

作用:

给每一行编号。


示例

sql 复制代码
SELECT
    name,
    score,
    ROW_NUMBER() OVER() AS rn
FROM student;

结果:

name score rn
张三 90 1
李四 85 2
王五 95 3

五、ORDER BY 很重要

窗口函数通常会配合:

sql 复制代码
ORDER BY

一起使用。


按成绩排名

sql 复制代码
SELECT
    name,
    score,
    ROW_NUMBER() OVER(
        ORDER BY score DESC
    ) AS rn
FROM student;

结果:

name score rn
王五 95 1
张三 90 2
李四 85 3

六、RANK() 和 ROW_NUMBER() 的区别

这是面试高频。

很多人容易搞混。


数据

name score
张三 90
李四 90
王五 85

1.ROW_NUMBER()

sql 复制代码
SELECT
    name,
    score,
    ROW_NUMBER() OVER(
        ORDER BY score DESC
    ) AS rn
FROM student;

结果:

name score rn
张三 90 1
李四 90 2
王五 85 3

特点:

sql 复制代码
即使分数相同,
排名也不会重复

2.RANK()

sql 复制代码
SELECT
    name,
    score,
    RANK() OVER(
        ORDER BY score DESC
    ) AS rk
FROM student;

结果:

name score rk
张三 90 1
李四 90 1
王五 85 3

特点:

sql 复制代码
相同分数排名一样,
但会跳名次

3.DENSE_RANK()

sql 复制代码
SELECT
    name,
    score,
    DENSE_RANK() OVER(
        ORDER BY score DESC
    ) AS dr
FROM student;

结果:

name score dr
张三 90 1
李四 90 1
王五 85 2

特点:

sql 复制代码
相同分数排名一样,
但不会跳名次

七、PARTITION BY(超级重要)

这个是窗口函数真正强大的地方。

你可以理解成:

"分组后,再在组内操作"

类似:

sql 复制代码
GROUP BY

但:

sql 复制代码
不会压缩数据

示例数据

name class score
张三 1班 90
李四 1班 85
王五 2班 95
赵六 2班 88

查询每个班的排名

sql 复制代码
SELECT
    name,
    class,
    score,
    ROW_NUMBER() OVER(
        PARTITION BY class
        ORDER BY score DESC
    ) AS rn
FROM student;

结果:

name class score rn
张三 1班 90 1
李四 1班 85 2
王五 2班 95 1
赵六 2班 88 2

八、SUM() 也能做窗口函数

这个很多人不知道。


查询累计工资

month salary
1 1000
2 1500
3 2000

累计求和

sql 复制代码
SELECT
    month,
    salary,
    SUM(salary) OVER(
        ORDER BY month
    ) AS total
FROM salary;

结果:

month salary total
1 1000 1000
2 1500 2500
3 2000 4500

九、窗口函数和 GROUP BY 的区别

这是核心。


GROUP BY

特点:

sql 复制代码
会压缩数据

比如:

sql 复制代码
SELECT class,
AVG(score)
FROM student
GROUP BY class;

最后:

sql 复制代码
一组只剩一行

窗口函数

特点:

sql 复制代码
保留原数据

比如:

sql 复制代码
SELECT
    name,
    score,
    AVG(score) OVER()
FROM student;

每个人的数据都还在。


十、实际开发中的经典场景


1.排行榜

sql 复制代码
RANK()
ROW_NUMBER()

2.分页

sql 复制代码
ROW_NUMBER()

3.TopN 查询

比如:

sql 复制代码
每个班前3名

4.累计统计

sql 复制代码
SUM() OVER()

5.环比分析

sql 复制代码
LAG()
LEAD()

十一、LAG() 和 LEAD()

这个在数据分析里特别常见。


LAG()

取上一行数据。

sql 复制代码
SELECT
    month,
    salary,
    LAG(salary) OVER(
        ORDER BY month
    ) AS prev_salary
FROM salary;

LEAD()

取下一行数据。

sql 复制代码
SELECT
    month,
    salary,
    LEAD(salary) OVER(
        ORDER BY month
    ) AS next_salary
FROM salary;

十二、新手最容易懵的点


1.OVER() 必须写

窗口函数一定要配:

sql 复制代码
OVER()

2.PARTITION BY 不是 GROUP BY

它只是:

sql 复制代码
组内操作

不会压缩数据。


3.ORDER BY 非常重要

很多窗口函数:

sql 复制代码
排名
累计
上一行
下一行

都依赖排序。


十三、最结

窗口函数本质:

在不减少原数据的情况下做统计。


最核心的几个函数

函数 作用
ROW_NUMBER() 行号
RANK() 排名(跳名次)
DENSE_RANK() 排名(不跳名次)
SUM() OVER() 累计求和
LAG() 上一行
LEAD() 下一行

最核心的语法

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

sql 复制代码
窗口函数 = 保留原数据 + 做统计
相关推荐
随风M记忆s14 分钟前
Redis支持事务吗?了解Redis的持久化机制吗?
数据库·redis·bootstrap
花青泽2 小时前
5-数据库-SQL注入-联合查询-关键字绕过-day13
数据库·sql
天桥下的卖艺者8 小时前
使用scitable包,两步生成逆概率删失权重(IPCW)
数据库·r语言
隔窗听雨眠8 小时前
AI原生数据库浪潮:国产数据库的架构重构与路径之争
数据库
数据库小学妹9 小时前
数据库选型实战:从数据类型到TCO成本,五维决策框架+九款产品横评
数据库·信创·国产数据库·数据库选型·oracle迁移
mldong9 小时前
从 mldong 到 jeeflow:一个工作流引擎的独立进化
后端
陈随易9 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
神龙天舞200110 小时前
MySQL 备库为什么会延迟好几个小时
android·数据库·mysql
Aaron - Wistron10 小时前
Web API C# (Furion版)带 单元测试
开发语言·后端·c#
卷无止境10 小时前
写代码这件事,到底该讲究点什么?
后端·python