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 复制代码
窗口函数 = 保留原数据 + 做统计
相关推荐
用户479492835691514 分钟前
claude Fable用不了?把Gpt 5.5pro接到你的claude code里
前端·后端
GetcharZp2 小时前
告别 Nginx 复杂配置!这款带 Web 面板的万能代理神器,让端口转发变得如此简单
后端
IT_陈寒4 小时前
React的useState居然还有这种坑?我差点删库跑路
前端·人工智能·后端
Pedantic5 小时前
SwiftUI 手势笔记
前端·后端
金銀銅鐵6 小时前
[Python] 从《千字文》中随机挑选汉字
后端·python
飘尘8 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
浏览器工程师9 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
行者全栈架构师9 小时前
Maven dependency:tree 的 8 个高级用法
java·后端
Chenyiax9 小时前
从一次请求看懂 OkHttp:架构、调度与连接管理
后端