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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值