MySQL 中的窗口函数

窗口函数是 MySQL 8.0 引入的一个重要特性,它可以在不改变数据行数 的前提下,对每一行数据进行聚合、排名、累积 等计算。与 GROUP BY 不同,窗口函数会保留所有原始行。

一、窗口函数

窗口函数的执行逻辑是:在每一行数据上,基于"窗口"(一个定义好的数据范围)进行计算,然后把计算结果的附加列(如排名、累计值等)直接写到对应的行上,不合并行。

语法结构

复制代码
SELECT
    列名,
    窗口函数() OVER (
        PARTITION BY 分区列    -- 分组
        ORDER BY 排序列        -- 排序
        ROWS/RANGE BETWEEN ... -- 窗口范围
    ) AS 别名
FROM 表名;

二、窗口函数的三大类别

1. 排名函数

函数 说明
ROW_NUMBER() 按顺序编号,不处理并列(1,2,3,4...)
RANK() 跳跃排名,并列后跳过(1,1,3,4...)
DENSE_RANK() 连续排名,并列后不跳过(1,1,2,3...)
NTILE(n) 将数据分成 n 组,返回组号
示例:按成绩排名
复制代码
-- 创建示例表
CREATE TABLE students (
    id INT,
    name VARCHAR(50),
    score INT,
    class VARCHAR(10)
);

INSERT INTO students VALUES
(1, 'Alice', 95, 'A'),
(2, 'Bob', 85, 'A'),
(3, 'Charlie', 95, 'A'),
(4, 'David', 78, 'B'),
(5, 'Eva', 92, 'B'),
(6, 'Frank', 85, 'B');

-- 各排名函数的对比
SELECT 
    name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK() OVER (ORDER BY score DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank,
    NTILE(3) OVER (ORDER BY score DESC) AS ntile
FROM students;

结果

复制代码
+---------+-------+---------+------+------------+-------+
| name    | score | row_num | rank | dense_rank | ntile |
+---------+-------+---------+------+------------+-------+
| Alice   | 95    | 1       | 1    | 1          | 1     |
| Charlie | 95    | 2       | 1    | 1          | 1     |
| Eva     | 92    | 3       | 3    | 2          | 2     |
| Bob     | 85    | 4       | 4    | 3          | 2     |
| Frank   | 85    | 5       | 4    | 3          | 3     |
| David   | 78    | 6       | 6    | 4          | 3     |
+---------+-------+---------+------+------------+-------+

区别

  • ROW_NUMBER():1,2,3,4,5,6

  • RANK():1,1,3,4,4,6

  • DENSE_RANK():1,1,2,3,3,4

  • NTILE(3):1,1,2,2,3,3

2. 聚合函数(作为窗口函数)

函数 说明
SUM() 窗口内的累计和
AVG() 窗口内的平均值
COUNT() 窗口内的行数
MAX() / MIN() 窗口内的最大值/最小值
示例:累计求和与移动平均
复制代码
SELECT 
    date,
    sales,
    SUM(sales) OVER (ORDER BY date) AS cumulative_sales,
    AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg_3
FROM sales_data;

3. 取值函数

函数 说明
LAG() 获取当前行之前的第 N 行
LEAD() 获取当前行之后的第 N 行
FIRST_VALUE() 窗口内的第一行
LAST_VALUE() 窗口内的最后一行
示例:计算环比增长率
复制代码
SELECT 
    month,
    revenue,
    LAG(revenue, 1) OVER (ORDER BY month) AS prev_month_revenue,
    ROUND(
        (revenue - LAG(revenue, 1) OVER (ORDER BY month)) / 
        LAG(revenue, 1) OVER (ORDER BY month) * 100, 2
    ) AS growth_rate_percent
FROM monthly_revenue;

三、PARTITION BY(分组计算)

PARTITION BY 将数据分组,窗口函数在每个分组内独立计算。

复制代码
SELECT 
    class,
    name,
    score,
    RANK() OVER (PARTITION BY class ORDER BY score DESC) AS rank_in_class
FROM students;

结果

复制代码
+-------+---------+-------+---------------+
| class | name    | score | rank_in_class |
+-------+---------+-------+---------------+
| A     | Alice   | 95    | 1             |
| A     | Charlie | 95    | 1             |
| A     | Bob     | 85    | 3             |
| B     | Eva     | 92    | 1             |
| B     | Frank   | 85    | 2             |
| B     | David   | 78    | 3             |
+-------+---------+-------+---------------+

四、窗口范围(Frame)

窗口范围决定计算时包含哪些行:

写法 说明
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 从开始到当前行(默认累计)
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 当前行及前 2 行
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 前 1 行 + 当前行 + 后 1 行
ROWS UNBOUNDED PRECEDING 从开始到当前行的简写
RANGE BETWEEN ... 基于值的范围(而不是行数)
复制代码
SELECT 
    date,
    sales,
    SUM(sales) OVER (ORDER BY date) AS cumulative,                  -- 默认从开始到当前
    SUM(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS last_3_days,
    SUM(sales) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS total_all
FROM sales_data;

五、窗口函数 vs GROUP BY

对比维度 GROUP BY 窗口函数 OVER()
行数变化 合并行,行数减少 保持原行数
结果位置 单独的结果行 附加在原行旁边
子查询需求 常需要子查询 不需要
适用场景 汇总统计 排名、累计、移动平均
复制代码
-- GROUP BY:只能看到汇总结果
SELECT department, AVG(salary) FROM employees GROUP BY department;

-- 窗口函数:每行都保留,同时显示平均工资
SELECT 
    employee_id,
    name,
    department,
    salary,
    AVG(salary) OVER (PARTITION BY department) AS dept_avg_salary
FROM employees;
相关推荐
小白男神1 天前
MySQL进阶学习四(存储过程、游标、触发器)
mysql
这个DBA有点耶1 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
DBA_G1 天前
从地面到云霄:GBase数据库在民航三大场景的落地实践
数据库
自由能燃气设备1 天前
商用全预混低氮冷凝锅炉免费方案vs付费方案对比+选型避坑指南
大数据·数据库·人工智能
科创致远1 天前
科创致远 ESOP 系统核心效能与实战价值展示
大数据·数据库·人工智能·精益工程
kybs19911 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
2601_962218612 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
程序猿_极客2 天前
【免费】分享一套优质的基于SSM的校园失物招领系统的设计与实现,源码+文档+视频详解(讲解)
java·mysql·ssm·课程设计·失物招领系统
张洛闻Eren2 天前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
于平安2 天前
MySQL-触发器
数据库·mysql