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 复制代码
窗口函数 = 保留原数据 + 做统计
相关推荐
~|Bernard|1 小时前
三,go语言中channel的底层原理
开发语言·后端·golang
武帝为此1 小时前
【软件开发日志介绍】
java·前端·数据库
likerhood1 小时前
Java 反射与注解的详细讲解
java·开发语言·数据库
todoitbo1 小时前
时序数据库选型指南(实战版):做一轮可落地评估
数据库·时序数据库
数智化精益手记局1 小时前
设备管理与维护包括哪些内容?详解设备管理与维护的流程
网络·数据库·人工智能
YL200404261 小时前
MySQL-基础篇-MySQL概述
数据库·mysql
毋语天1 小时前
Docker 环境下 Milvus 向量数据库的稳定部署与常见问题
数据库·docker·milvus
IMPYLH1 小时前
Linux 的 uname 命令
linux·运维·服务器·数据库·bash
Raina测试1 小时前
基于Skills的接口自动化测试方案|新增 MySQL 断言,实现接口 + 数据库双校验
软件测试·数据库·接口自动化测试·测试工程师·skill·ai测试