MySQL的窗口函数(Window Functions)
一、窗口函数核心概念
-
窗口(Window)
窗口是数据行的集合,由OVER()
子句定义。它决定了函数计算的“数据范围”,可以是一个分区的全部行、当前行前后的行,或动态变化的子集。 -
语法结构
SELECT window_function(column) OVER ([PARTITION BY partition_expression][ORDER BY order_expression [ASC|DESC]][frame_clause]) AS alias FROM table;
- PARTITION BY:将数据划分为多个分区(类似
GROUP BY
),函数在每个分区内独立计算。 - ORDER BY:定义分区内数据的排序方式,影响窗口函数的计算顺序。
- frame_clause:定义窗口的具体范围(如当前行及其前后N行)。
- PARTITION BY:将数据划分为多个分区(类似
二、窗口函数分类及示例
1. 聚合类窗口函数
普通聚合函数(如SUM
、AVG
、COUNT
)结合OVER()
使用,实现累计、移动平均等效果。
示例:计算累计销售额
SELECT order_date,amount,SUM(amount) OVER (ORDER BY order_date) AS cumulative_sum
FROM sales;
结果:
order_date | amount | cumulative_sum
-------------------------------------
2023-01-01 | 100 | 100
2023-01-02 | 200 | 300
2023-01-03 | 150 | 450
2. 排名类窗口函数
-
ROW_NUMBER()
:为每行分配唯一序号(相同值也会不同)。 -
RANK()
:相同值的行排名相同,后续序号跳跃(如1,1,3)。 -
DENSE_RANK()
:相同值的行排名相同,后续序号连续(如1,1,2)。
示例:按销售额排名
SELECT product,sales,ROW_NUMBER() OVER (ORDER BY sales DESC) AS row_num,RANK() OVER (ORDER BY sales DESC) AS rank,DENSE_RANK() OVER (ORDER BY sales DESC) AS dense_rank
FROM products;
结果:
product | sales | row_num | rank | dense_rank
--------------------------------------------
A | 500 | 1 | 1 | 1
B | 500 | 2 | 1 | 1
C | 400 | 3 | 3 | 2
3. 分布类窗口函数
-
PERCENT_RANK()
:计算行的相对排名百分比(范围[0,1])。 -
CUME_DIST()
:计算行的累积分布(当前行及其之前行的占比)。
示例:计算销售额分布
SELECT product,sales,PERCENT_RANK() OVER (ORDER BY sales) AS percent_rank,CUME_DIST() OVER (ORDER BY sales) AS cume_dist
FROM products;
4. 前后函数
-
LAG(column, N)
:获取当前行前N行的值。 -
LEAD(column, N)
:获取当前行后N行的值。
示例:计算销售额环比增长
SELECT month,sales,LAG(sales, 1) OVER (ORDER BY month) AS prev_sales,(sales - LAG(sales, 1) OVER (ORDER BY month)) / LAG(sales, 1) OVER (ORDER BY month) AS growth_rate
FROM monthly_sales;
结果:
month | sales | prev_sales | growth_rate
----------------------------------------
Jan | 1000 | NULL | NULL
Feb | 1200 | 1000 | 0.2
Mar | 1500 | 1200 | 0.25
三、窗口帧(Frame Clause)
通过ROWS
或RANGE
定义窗口的具体范围:
-
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
:从分区开始到当前行。 -
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
:当前行的前一行到后一行。
示例:计算3个月移动平均
SELECT month,sales,AVG(sales) OVER (ORDER BY monthROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM monthly_sales;
结果:
month | sales | moving_avg
---------------------------
Jan | 100 | 100.0
Feb | 200 | 150.0
Mar | 150 | 150.0
Apr | 300 | 216.7
四、使用场景
- 排名与分组排名:按部门、时间等分区后排序。
- 累计计算:累计销售额、年累计增长率。
- 移动统计:移动平均、移动求和。
- 数据对比:当前行与前一行的差值或比率。
五、注意事项
- MySQL版本:窗口函数需MySQL 8.0+,旧版本不支持。
- 性能优化:合理使用索引和分区,避免全表扫描。
- 执行顺序:窗口函数在
WHERE
、GROUP BY
、HAVING
之后执行。