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 编程的重要工具,推荐优先使用。