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 编程的重要工具,推荐优先使用。
相关推荐
麦聪聊数据17 小时前
MySQL并发与锁:从“防止超卖”到排查“死锁”
数据库·sql·mysql
黑白极客21 小时前
怎么给字符串字段加索引?日志系统 一条更新语句是怎么执行的
java·数据库·sql·mysql·引擎
爬山算法1 天前
Hibernate(31)Hibernate的原生SQL查询是什么?
数据库·sql·hibernate
l1t1 天前
NineData第三届数据库编程大赛:用一条 SQL 解数独问题我的参赛程序
数据库·人工智能·sql·算法·postgresql·oracle·数独
施嘉伟1 天前
一次生产环境 SQL 不走索引的排查过程
数据库·sql
Web项目开发1 天前
Mysql创建索引的SQL脚本,复制粘贴即可使用
数据库·sql·mysql
Waloo1 天前
SQL Server 2017 EXISTS 关键字 完整用法详解(最全 + 最优写法 + 性能对比)
sql·sql server
周末吃鱼1 天前
mysql8.0支持CURRENT_DATE如何写
数据库·sql·mysql
尽兴-1 天前
SQL 执行失败如何回滚?事务已提交还能恢复吗?——MySQL 误操作数据恢复全指南
sql·mysql·binlog·undolog·redolog
l1t1 天前
DeepSeek辅助编写添加了矛盾检测的数独求解SQL
数据库·sql·算法·数独