200字
窗口函数
2024-09-22
2025-05-09

窗口函数是 SQL 中用于分析数据的强大工具,它允许在查询中对一组行执行计算,而不需要将其分组成单个结果。窗口函数可以对查询结果的每一行提供额外的信息,这在很多分析场景中都非常有用。

  1. 定义:窗口函数在 SQL 查询中对数据集中的行进行计算,但与聚合函数不同,窗口函数不会减少结果集的行数。每一行都可以保留在结果中,并且可以基于窗口的定义来计算聚合值。

  2. 窗口的组成

    • PARTITION BY:用于将结果集分成多个分区(组)。每个分区内的行将独立进行计算。
    • ORDER BY:在每个分区内定义行的顺序。窗口函数的计算是基于这个顺序进行的。
    • RANGE 或 ROWS:定义窗口的范围,可以指定行数或基于值(如日期)进行计算。
  3. 窗口函数的基本语法

    <窗口函数> OVER (
        [PARTITION BY <列名>]
        [ORDER BY <列名>]
        [ROWS|RANGE BETWEEN ...]
    )
    
  4. 常见的专用窗口函数

    函数分类说明
    RANK排序函数类似于排名,并列的结果序号可以重复,序号不连续
    DENSE_RANK排序函数类似于排名,并列的结果序号可以重复,序号连续
    ROW_NUMBER排序函数对该分组下的所有结果作一个排序,基于该分组给一个行数
    PERCENT_RANK分布函数每行按照公式 (rank-1) / (rows-1) 进行计算
    CUME_DIST分布函数分组内小于、等于当前 rank 值的行数 / 分组内总行数

  5. LeetCode例题
    1321. 餐馆营业额变化增长

    表: Customer
    +---------------+---------+
    | Column Name   | Type    |
    +---------------+---------+
    | customer_id   | int     |
    | name          | varchar |
    | visited_on    | date    |
    | amount        | int     |
    +---------------+---------+
    在 SQL 中,(customer_id, visited_on) 是该表的主键。
    该表包含一家餐馆的顾客交易数据。
    visited_on 表示 (customer_id) 的顾客在 visited_on 那天访问了餐馆。
    amount 是一个顾客某一天的消费总额。
     
    你是餐馆的老板,现在你想分析一下可能的营业额变化增长(每天至少有一位顾客)。
    计算以 7 天(某日期 + 该日期前的 6 天)为一个时间段的顾客消费平均值。average_amount 要 保留两位小数。
    结果按 visited_on 升序排序。
    返回结果格式的例子如下。
    
     
    
    示例 1:
    
    输入:
    Customer 表:
    +-------------+--------------+--------------+-------------+
    | customer_id | name         | visited_on   | amount      |
    +-------------+--------------+--------------+-------------+
    | 1           | Jhon         | 2019-01-01   | 100         |
    | 2           | Daniel       | 2019-01-02   | 110         |
    | 3           | Jade         | 2019-01-03   | 120         |
    | 4           | Khaled       | 2019-01-04   | 130         |
    | 5           | Winston      | 2019-01-05   | 110         | 
    | 6           | Elvis        | 2019-01-06   | 140         | 
    | 7           | Anna         | 2019-01-07   | 150         |
    | 8           | Maria        | 2019-01-08   | 80          |
    | 9           | Jaze         | 2019-01-09   | 110         | 
    | 1           | Jhon         | 2019-01-10   | 130         | 
    | 3           | Jade         | 2019-01-10   | 150         | 
    +-------------+--------------+--------------+-------------+
    输出:
    +--------------+--------------+----------------+
    | visited_on   | amount       | average_amount |
    +--------------+--------------+----------------+
    | 2019-01-07   | 860          | 122.86         |
    | 2019-01-08   | 840          | 120            |
    | 2019-01-09   | 840          | 120            |
    | 2019-01-10   | 1000         | 142.86         |
    +--------------+--------------+----------------+
    #解释:
    #第一个七天消费平均值从 2019-01-01 到 2019-01-07 是restaurant-growth/restaurant-growth/ (100 + 110 + 120 + 130 + 110 + 140 + 150)/7 = 122.86
    #第二个七天消费平均值从 2019-01-02 到 2019-01-08 是 (110 + 120 + 130 + 110 + 140 + 150 + 80)/7 = 120
    #第三个七天消费平均值从 2019-01-03 到 2019-01-09 是 (120 + 130 + 110 + 140 + 150 + 80 + 110)/7 = 120
    #第四个七天消费平均值从 2019-01-04 到 2019-01-10 是 (130 + 110 + 140 + 150 + 80 + 110 + 130 + 150)/7 = 142.86
    

    解法:

    select
    visited_on, 
    sum_amount as amount,
    round(sum_amount/7,2) as average_amount
    from
    (
        select 
        distinct visited_on,
        #拿到前六天和当天的合
        SUM(amount) over(order by visited_on asc RANGE between INTERVAL 6 DAY preceding and current row) as sum_amount,
        dense_rank() over(order by visited_on asc) as rank_num
        from Customer
    ) t
    where
    rank_num >= 7
    
  6. ROWS BETWEEN子句常见关键字含义

    关键字含义
    PRECEDING当前行数往前
    FOLLOWING当前行数往后
    CURRENT ROW当前行
    UNBOUNDED起点(一般结合PRECEDING,FOLLOWING使用)
    UNBOUNDED PRECEDING表示该窗口最前面的行(起点)
    UNBOUNDED FOLLOWING表示该窗口最后面的行(终点)
  7. 优势

    • 保留行信息:窗口函数不会丢失行信息,允许分析每一行的上下文。
    • 灵活性:可以对数据进行多维度分析,比如按时间、区域等进行分组。
    • 性能:在大数据集上,使用窗口函数可以更有效地处理数据分析。

评论