MySQL8.0窗口函数实战指南

MySQL 8.0新增加了窗口函数的功能,窗口函数方便了SQL的编码,也是MySQL 8.0的亮点之一。

ROW_NUMBER()

ROW_NUMBER() (分组)排序编号,按照表中某一字段分组,再按照某一字段排序,对已有的数据生成一个编号。

任何一个窗口函数,都可以分组统计或者不分组统计。语法格式为

row_number()over(partition by 字段名 order by 字段名) as 编号;

【例1】创建用户订单表order1, 并插入10条记录,基于order1查询 每位用户最新的订单信息。

sql 复制代码
CREATE TABLE order1 (
    order_id INT PRIMARY KEY,
    user_no VARCHAR(20),
    amount DECIMAL(10,2),
    create_date DATETIME
);

-- 插入测试数据
INSERT INTO order1 VALUES
(1, 'U001', 100.00, '2025-12-01 10:00:00'),
(2, 'U001', 150.00, '2025-12-05 11:00:00'),
(3, 'U002', 200.00, '2025-12-03 09:00:00');


SELECT 
    order_id, user_no, amount, create_date
FROM (
    SELECT 
        ROW_NUMBER() OVER (PARTITION BY user_no ORDER BY create_date DESC) AS row_num,
        order_id,
        user_no,
        amount,
        create_date
    FROM order1
) t
WHERE t.row_num = 1;

【例2】查询学生选课表中每门课程的最高分,按照课程编号分组后按成绩降序排列,返回每一组数据的第一条记录的成绩,即每门课的最高分。

sql 复制代码
SELECT * FROM 
 (SELECT ROW_NUMBER()OVER(PARTITION BY 课程号 ORDER BY 成绩 DESC) AS row_num,
学号,课程号,成绩 FROM choose )  t WHERE row_num=1;

✅ 1. RANK()

🔹 功能:

为每行数据分配一个排名,相同值会获得相同的排名,但后续排名会跳过(即不连续)。

📌 与 ROW_NUMBER() 不同:ROW_NUMBER() 每行都不同,RANK() 相同值排名相同。

🔹 语法:
sql 复制代码
RANK() OVER (PARTITION BY 分组字段 ORDER BY 排序字段)
🌰 示例:
sql 复制代码
SELECT 
    姓名,
    成绩,
    RANK() OVER (ORDER BY 成绩 DESC) AS 排名
FROM student;
姓名 成绩 排名
张三 90 1
李四 90 1
王五 85 3

✅ 两个 90 分都是第 1 名,下一个为第 3 名(跳过了 2)。


✅ 2. AVG()SUM() 等聚合函数在窗口函数中的增强

🔹 功能:

将常见的聚合函数(如 AVG, SUM, COUNT, MAX, MIN)用于窗口函数 中,实现"累计统计 "或"分组内统计"。

🔹 语法:
sql 复制代码
AVG(列名) OVER (
    [PARTITION BY 分组字段]
    [ORDER BY 排序字段]
    [ROWS BETWEEN ...]  -- 可选:定义窗口范围
)
🌰 示例:计算每个学生的累计成绩总和
sql 复制代码
SELECT 
    学号,
    课程号,
    成绩,
    SUM(成绩) OVER (PARTITION BY 学号 ORDER BY 课程号) AS 累计总分
FROM choose;

这样可以查看某个学生每门课学完后的总分变化趋势。


✅ 3. NILE(n) ------ 应该是 NTILE(n)

🔹 功能:

将结果集按顺序分成 n等大小的桶(bucket),并返回每个行所在的桶编号。

⚠️ 注意:原图写的是 NIILE(n),应为笔误,正确是 NTILE(n)

🔹 语法:
sql 复制代码
NTILE(n) OVER (ORDER BY 字段)
🌰 示例:把学生成绩分为 3 个等级(上、中、下)
sql 复制代码
SELECT 
    姓名,
    成绩,
    NTILE(3) OVER (ORDER BY 成绩 DESC) AS 等级
FROM student;
姓名 成绩 等级
张三 95 1
李四 90 1
王五 85 2
赵六 80 2
钱七 75 3

✅ 前 2 名 → 等级 1,中间 2 名 → 等级 2,最后 1 名 → 等级 3。


✅ 4. LAG()LEAD() 函数

🔹 功能:
  • LAG():获取当前行前面某一行的值(偏移量可指定);
  • LEAD():获取当前行后面某一行的值。

💡 常用于对比相邻记录,如"本月 vs 上月"、"增长率"分析。

🔹 语法:
sql 复制代码
LAG(列名, 偏移量, 默认值) OVER (ORDER BY 字段)
LEAD(列名, 偏移量, 默认值) OVER (ORDER BY 字段)
🌰 示例:计算每月销售额环比增长
sql 复制代码
SELECT 
    月份,
    销售额,
    LAG(销售额, 1) OVER (ORDER BY 月份) AS 上月销售额,
    ROUND((销售额 - LAG(销售额, 1) OVER (ORDER BY 月份)) / LAG(销售额, 1) OVER (ORDER BY 月份), 2) AS 增长率
FROM sales;

✅ 5. CTE 公用表表达式(Common Table Expression)

🔹 功能:

允许你定义一个临时的结果集(类似子查询),并在后续查询中多次引用它。

提升 SQL 可读性和复用性。

🔹 语法:
sql 复制代码
WITH 别名 AS (
    SELECT ...
)
SELECT ... FROM 别名;
🌰 示例:使用 CTE 查找每个用户的最新订单
sql 复制代码
WITH RankedOrders AS (
    SELECT 
        ROW_NUMBER() OVER (PARTITION BY user_no ORDER BY create_date DESC) AS rn,
        order_id, user_no, amount, create_date
    FROM dingdan
)
SELECT order_id, user_no, amount, create_date
FROM RankedOrders
WHERE rn = 1;

✅ 比嵌套子查询更清晰易懂。


✅ 总结(表格对比)

函数/特性 作用 典型场景
RANK() 排名(相同值同名,跳过后续) 成绩排名
AVG()/SUM() 窗口 累计统计、分组内汇总 累计销售额
NTILE(n) 分桶(等分) 分成高/中/低档
LAG()/LEAD() 获取前后行数据 环比分析
CTE 定义临时结果集 复杂查询拆解

💡 小贴士

  • 所有这些功能都在 MySQL 8.0+ 中支持;
  • 窗口函数不能直接在 WHERE 中使用,需通过 CTE 或子查询包裹;
  • CTE 是现代 SQL 编程的重要工具,推荐优先使用。
相关推荐
·云扬·6 小时前
InnoDB事务隔离级别与加锁机制深度解析
数据库·sql·mysql
大佬,救命!!!8 小时前
python对应sql操作
开发语言·python·sql·学习笔记·学习方法
IT枫斗者11 小时前
Java 开发实战:从分层架构到性能优化(Spring Boot + MyBatis-Plus + Redis + JWT)
java·spring boot·sql·mysql·性能优化·架构
悟能不能悟11 小时前
mybatis sql where a=#{a},如果a为null,会返回什么
数据库·sql·mybatis
l1t11 小时前
豆包解读论文:将具有分支和循环控制流的命令式程序转换为标准SQL1999的公共表表达式
开发语言·数据库·人工智能·python·sql·postgresql·duckdb
rannn_11111 小时前
【SQL题解】力扣高频 SQL 50题|DAY1
后端·sql·题解
武昌库里写JAVA12 小时前
Java设计模式-(创建型)抽象工厂模式
java·vue.js·spring boot·后端·sql
正在走向自律15 小时前
Oracle迁移至金仓数据库:PL/SQL匿名块执行失败的深度排查指南
数据库·sql·oracle·国产数据库·电科金仓
薛不痒1 天前
MySQL中使用SQL语言
数据库·sql·mysql