MySQL窗口函数

窗口函数

窗口函数是用于在查询结果集中执行复杂计算的函数。与聚合函数不同,窗口函数不会将多行数据压缩成单行数据,而是在保留行细节的同时进行计算。窗口函数允许在查询结果集中的某一部分(称为窗口或帧)内进行操作,从而实现如累计和、排名、移动平均等复杂计算。

除了常见的聚合函数,窗口函数还有一些特殊的函数。例如,RANK()DENSE_RANK()ROW_NUMBER()等函数都是窗口函数特有的,它们可以在数据分析中提供更多的灵活性和便利性。这些函数通常与OVER子句一起使用,以确定窗口的范围和排序。

其基本语法为:

函数名 OVER ([PARTITION BY 列名] [ORDER BY 列名][ACE|EDSC])

其中函数名包括聚合函数和窗口函数专用的函数。

假如我们有一张名为 sales 的表,如下所示:

+---------+------------+--------+----------------+
| sale_id | sale_date  | amount | salesperson_id |
+---------+------------+--------+----------------+
|       1 | 2024-06-01 | 100.00 |              1 |
|       2 | 2024-06-02 | 150.00 |              2 |
|       3 | 2024-06-02 | 200.00 |              1 |
|       4 | 2024-06-03 | 120.00 |              3 |
|       5 | 2024-06-03 | 180.00 |              2 |
|       6 | 2024-06-04 | 220.00 |              1 |
|       7 | 2024-06-04 | 100.00 |              1 |
|       8 | 2024-06-05 | 150.00 |              2 |
+---------+------------+--------+----------------+

RANK()、DENSE_RANK()、ROW_NUMBER()

先来看一下着三个函数的区别。

函数描述示例
RANK()RANK() 函数返回唯一的排名值,但在遇到相同的值时会跳过一些排名。例如,如果有两行具有相同的值,则它们都会被赋予相同的排名,但下一个排名值将会跳过。1, 2, 2, 4
DENSE_RANK()DENSE_RANK() 函数也返回唯一的排名值,但不会跳过任何排名。即使有两行具有相同的值,下一个排名值也不会被跳过。1, 2, 2, 3
ROW_NUMBER()ROW_NUMBER() 函数为结果集中的每一行分配一个唯一的数字。即使有两行具有相同的值,它们也会被赋予不同的数字。1, 2, 3, 4

下面我们逐一的进行介绍:

  • **RANK():**为相同数值的行分配相同的排名,并跳过下一个排名。

    第一种方式:

    select sale_id, sale_date, amount, 
           rank() over (partition by sale_date order by amount desc) as sales_rank
    from sales;
    
    +---------+------------+--------+------------+
    | sale_id | sale_date  | amount | sales_rank |
    +---------+------------+--------+------------+
    |       1 | 2024-06-01 | 100.00 |          1 |
    |       3 | 2024-06-02 | 200.00 |          1 |
    |       2 | 2024-06-02 | 150.00 |          2 |
    |       5 | 2024-06-03 | 180.00 |          1 |
    |       4 | 2024-06-03 | 120.00 |          2 |
    |       6 | 2024-06-04 | 220.00 |          1 |
    |       7 | 2024-06-04 | 100.00 |          2 |
    |       8 | 2024-06-05 | 150.00 |          1 |
    +---------+------------+--------+------------+
    8 rows in set (0.00 sec)
    

    第二种方式:

    select sale_id, sale_date, amount, 
           rank() over (order by amount desc) as sales_rank
    from sales;
    
    +---------+------------+--------+------------+
    | sale_id | sale_date  | amount | sales_rank |
    +---------+------------+--------+------------+
    |       6 | 2024-06-04 | 220.00 |          1 |
    |       3 | 2024-06-02 | 200.00 |          2 |
    |       5 | 2024-06-03 | 180.00 |          3 |
    |       2 | 2024-06-02 | 150.00 |          4 |
    |       8 | 2024-06-05 | 150.00 |          4 |
    |       4 | 2024-06-03 | 120.00 |          6 |
    |       1 | 2024-06-01 | 100.00 |          7 |
    |       7 | 2024-06-04 | 100.00 |          7 |
    +---------+------------+--------+------------+
    8 rows in set (0.00 sec)
    

    两种方式的结果不同,这是因为partition by sale_date,设定了排序的对象范围,才会出现上面的结果。

    同时从这两个例子中也可以看到partition by不是必须的,即使不指定也能正常使用窗口函数。也能看出窗口函数同时具备分组和排序两种功能。

  • **DENSE_RANK():**为相同数值的行分配相同的排名,但不跳过下一个排名。

    select sale_id, sale_date, amount, 
           dense_rank() over (order by amount desc) as dense_sales_rank
    from sales;
    
    +---------+------------+--------+------------------+
    | sale_id | sale_date  | amount | dense_sales_rank |
    +---------+------------+--------+------------------+
    |       6 | 2024-06-04 | 220.00 |                1 |
    |       3 | 2024-06-02 | 200.00 |                2 |
    |       5 | 2024-06-03 | 180.00 |                3 |
    |       2 | 2024-06-02 | 150.00 |                4 |
    |       8 | 2024-06-05 | 150.00 |                4 |
    |       4 | 2024-06-03 | 120.00 |                5 |
    |       1 | 2024-06-01 | 100.00 |                6 |
    |       7 | 2024-06-04 | 100.00 |                6 |
    +---------+------------+--------+------------------+
    8 rows in set (0.00 sec)
    
  • ROW_NUMBER(): 给查询结果中的每一行分配一个唯一的序号。

    select sale_id, sale_date, amount, 
           row_number() over (order by sale_date) as row_num
    from sales;
    
    +---------+------------+--------+---------+
    | sale_id | sale_date  | amount | row_num |
    +---------+------------+--------+---------+
    |       1 | 2024-06-01 | 100.00 |       1 |
    |       2 | 2024-06-02 | 150.00 |       2 |
    |       3 | 2024-06-02 | 200.00 |       3 |
    |       4 | 2024-06-03 | 120.00 |       4 |
    |       5 | 2024-06-03 | 180.00 |       5 |
    |       6 | 2024-06-04 | 220.00 |       6 |
    |       7 | 2024-06-04 | 100.00 |       7 |
    |       8 | 2024-06-05 | 150.00 |       8 |
    +---------+------------+--------+---------+
    8 rows in set (0.00 sec)
    

常用的聚合函数另外的用法

常用的聚合函数可以帮助我们对窗口内的数据进行汇总和计算,而不需要分组。这些函数能够以更灵活的方式处理数据,例如计算累积总和、计算移动平均等。

以下是一些常用的聚合函数在窗口函数中的应用示例:

  • SUM() OVER(): 计算指定列的累积总和。

    -- 计算累积总和。
    select sale_id, sale_date, amount,
           sum(amount) over (order by sale_date) as running_total
    from sales;
    
    +---------+------------+--------+---------------+
    | sale_id | sale_date  | amount | running_total |
    +---------+------------+--------+---------------+
    |       1 | 2024-06-01 | 100.00 |        100.00 |
    |       2 | 2024-06-02 | 150.00 |        450.00 |
    |       3 | 2024-06-02 | 200.00 |        450.00 |
    |       4 | 2024-06-03 | 120.00 |        750.00 |
    |       5 | 2024-06-03 | 180.00 |        750.00 |
    |       6 | 2024-06-04 | 220.00 |       1070.00 |
    |       7 | 2024-06-04 | 100.00 |       1070.00 |
    |       8 | 2024-06-05 | 150.00 |       1220.00 |
    +---------+------------+--------+---------------+
    8 rows in set (0.00 sec)
    
    -- 计算分天累积总和。
    select sale_id, sale_date, amount,
           sum(amount) over (partition by sale_date order by sale_date) as running_total
    from sales
    order by
        sale_date, sale_id;
    
    +---------+------------+--------+----------------------+
    | sale_id | sale_date  | amount | daily_cumulative_sum |
    +---------+------------+--------+----------------------+
    |       1 | 2024-06-01 | 100.00 |               100.00 |
    |       2 | 2024-06-02 | 150.00 |               150.00 |
    |       3 | 2024-06-02 | 200.00 |               350.00 |
    |       4 | 2024-06-03 | 120.00 |               120.00 |
    |       5 | 2024-06-03 | 180.00 |               300.00 |
    |       6 | 2024-06-04 | 220.00 |               220.00 |
    |       7 | 2024-06-04 | 100.00 |               320.00 |
    |       8 | 2024-06-05 | 150.00 |               150.00 |
    +---------+------------+--------+----------------------+
    8 rows in set (0.00 sec)
    

    巧妙的利用partition by会有出色的效果

  • AVG() OVER(): 计算指定列的移动平均值。

    select sale_id, sale_date, amount,
           avg(amount) over (order by sale_date rows between 1 preceding and current row) as moving_avg
    from sales;
    
    +---------+------------+--------+------------+
    | sale_id | sale_date  | amount | moving_avg |
    +---------+------------+--------+------------+
    |       1 | 2024-06-01 | 100.00 | 100.000000 |
    |       2 | 2024-06-02 | 150.00 | 125.000000 |
    |       3 | 2024-06-02 | 200.00 | 175.000000 |
    |       4 | 2024-06-03 | 120.00 | 160.000000 |
    |       5 | 2024-06-03 | 180.00 | 150.000000 |
    |       6 | 2024-06-04 | 220.00 | 200.000000 |
    |       7 | 2024-06-04 | 100.00 | 160.000000 |
    |       8 | 2024-06-05 | 150.00 | 125.000000 |
    +---------+------------+--------+------------+
    8 rows in set (0.00 sec)
    

    这里计算每笔销售及其前一笔销售的平均销售额,这被称为移动平均值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值