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 复制代码
窗口函数 = 保留原数据 + 做统计
相关推荐
万少11 小时前
Claude Code 任务结束会自己喊你:一个 Stop Hook 搞定提示音
前端·后端·代码规范
rising start11 小时前
Redis 哨兵模式(Sentinel)
数据库·redis·sentinel
仙俊红11 小时前
spring有多个对象时如何注入
java·后端·spring
梦幻通灵11 小时前
Mysql处理锁冲突Lock conflict可用方案
数据库·mysql
小碗羊肉11 小时前
【Redis | 第五篇】分布式锁
数据库·redis·分布式
Java爱好狂.11 小时前
Redis高级笔记:深入浅出Java面试高频考点!
java·数据库·redis·后端·java面试·java程序员·java八股文
念恒1230611 小时前
MySQL事务(2)---事务隔离级别
数据库·mysql
IT_陈寒11 小时前
React hooks闭包陷阱把我坑惨了,原来这才是正确用法
前端·人工智能·后端
rising start11 小时前
深度解析 Redis 主从复制
数据库·redis·主从复制
会编程的土豆11 小时前
Go 里 interface 为什么能比较?到底在比什么?
开发语言·后端·golang