目录
1. ROW_NUMBER()
2. RANK()
3. DENSE_RANK()
4. NTILE(n)
5. LAG() 和 LEAD()
6. FIRST_VALUE() 和 LAST_VALUE()
总结
MySQL中的窗口函数(Window Functions)允许用户对一个结果集的窗口(或分区)执行计算,这些窗口是由查询的每行定义的。窗口函数在SQL标准中定义,并且在MySQL 8.0及更高版本中可用。窗口函数为每行返回一个值,这个值是基于该行在其分区或整个结果集中的位置计算得出的。
窗口函数通常与OVER()子句一起使用,OVER()子句定义了窗口的范围或边界。over()里头的分组以及排序的执行晚于外头 where 、group by、 order by 的执行。
以原始sales表数据为例,介绍一些常见的MySQL窗口函数:
product_id |
sale_date |
amount |
1 |
2023-01-01 |
100.00 |
2 |
2023-01-02 |
150.00 |
3 |
2023-01-03 |
150.00 |
1 |
2023-01-04 |
120.00 |
2 |
2023-01-05 |
180.00 |
- 为结果集中的每一行分配一个唯一的序号。
SELECT
product_id,
sale_date,
amount,
ROW_NUMBER() OVER (ORDER BY sale_date) AS row_num
FROM sales;
查询结果
product_id |
sale_date |
amount |
row_num |
1 |
2023-01-01 |
100.00 |
1 |
2 |
2023-01-02 |
150.00 |
2 |
3 |
2023-01-03 |
150.00 |
3 |
1 |
2023-01-04 |
120.00 |
4 |
2 |
2023-01-05 |
180.00 |
5 |
- 为结果集中的每一行分配一个唯一的排名,对于平级记录会留下空位。例如,如果有两行并列第一,则它们都会被分配排名1,下一行将被分配排名3(而不是2)。
SELECT
product_id,
sale_date,
amount,
RANK() OVER (ORDER BY amount DESC) AS rank
FROM sales;
查询结果
product_id |
sale_date |
amount |
rank |
2 |
2023-01-05 |
180.00 |
1 |
2 |
2023-01-02 |
150.00 |
2 |
3 |
2023-01-03 |
150.00 |
2 |
1 |
2023-01-04 |
120.00 |
4 |
1 |
2023-01-01 |
100.00 |
5 |
- 类似于RANK(),但不会留下空位。
SELECT
product_id,
sale_date,
amount,
DENSE_RANK() OVER (ORDER BY amount DESC) AS dense_rank
FROM sales;
查询结果
product_id |
sale_date |
amount |
dense_rank |
2 |
2023-01-05 |
180.00 |
1 |
2 |
2023-01-02 |
150.00 |
2 |
3 |
2023-01-03 |
150.00 |
2 |
1 |
2023-01-04 |
120.00 |
3 |
1 |
2023-01-01 |
100.00 |
4 |
- 将结果集分为“n”个大致相等的部分,并为每行分配一个桶号。
SELECT
product_id,
sale_date,
amount,
NTILE(2) OVER (ORDER BY sale_date) AS quarter
FROM sales;
查询结果
product_id |
sale_date |
amount |
quarter |
1 |
2023-01-01 |
100.00 |
1 |
2 |
2023-01-02 |
150.00 |
1 |
3 |
2023-01-03 |
150.00 |
2 |
1 |
2023-01-04 |
120.00 |
2 |
2 |
2023-01-05 |
180.00 |
2 |
- 允许访问前面或后面的行而无需自连接。
SELECT
product_id,
sale_date,
amount,
LAG(amount) OVER (PARTITION BY product_id ORDER BY sale_date) AS previous_amount
FROM sales;
查询结果
product_id |
sale_date |
amount |
previous_amount |
1 |
2023-01-01 |
100.00 |
NULL |
2 |
2023-01-02 |
150.00 |
NULL |
3 |
2023-01-03 |
150.00 |
NULL |
1 |
2023-01-04 |
120.00 |
100.00 |
2 |
2023-01-05 |
180.00 |
150.00 |
- 返回窗口内的第一个或最后一个值。
SELECT
product_id,
sale_date,
amount,
FIRST_VALUE(amount) OVER (PARTITION BY product_id ORDER BY sale_date) AS first_amount
FROM sales;
查询结果
product_id |
sale_date |
amount |
first_amount |
1 |
2023-01-01 |
100.00 |
100.00 |
2 |
2023-01-02 |
150.00 |
150.00 |
3 |
2023-01-03 |
150.00 |
150.00 |
1 |
2023-01-04 |
120.00 |
100.00 |
2 |
2023-01-05 |
180.00 |
150.00 |
请注意,OVER()子句可以包含PARTITION BY来定义窗口内的分区,每个分区都独立地应用窗口函数。
SELECT
product_id,
sale_date,
amount,
SUM(amount) OVER (PARTITION BY product_id) AS total_amount_per_product
FROM sales;
在这个例子中,SUM(amount)函数在每个product_id分区内独立计算。
查询结果
product_id |
sale_date |
amount |
total_amount_per_product |
1 |
2023-01-01 |
100.00 |
220.00 |
2 |
2023-01-02 |
150.00 |
330.00 |
3 |
2023-01-03 |
150.00 |
150.00 |
1 |
2023-01-04 |
120.00 |
220.00 |
2 |
2023-01-05 |
180.00 |
330.00 |
窗口函数在处理涉及多个行但不需要进行复杂分组或连接的问题时非常有用,比如计算运行总计、排名或查找前一行/后一行的值等。