窗口函数是 SQL 中用于分析数据的强大工具,它允许在查询中对一组行执行计算,而不需要将其分组成单个结果。窗口函数可以对查询结果的每一行提供额外的信息,这在很多分析场景中都非常有用。
-
定义:窗口函数在 SQL 查询中对数据集中的行进行计算,但与聚合函数不同,窗口函数不会减少结果集的行数。每一行都可以保留在结果中,并且可以基于窗口的定义来计算聚合值。
-
窗口的组成:
- PARTITION BY:用于将结果集分成多个分区(组)。每个分区内的行将独立进行计算。
- ORDER BY:在每个分区内定义行的顺序。窗口函数的计算是基于这个顺序进行的。
- RANGE 或 ROWS:定义窗口的范围,可以指定行数或基于值(如日期)进行计算。
-
窗口函数的基本语法:
<窗口函数> OVER ( [PARTITION BY <列名>] [ORDER BY <列名>] [ROWS|RANGE BETWEEN ...] ) -
常见的专用窗口函数:
函数 分类 说明 RANK 排序函数 类似于排名,并列的结果序号可以重复,序号不连续 DENSE_RANK 排序函数 类似于排名,并列的结果序号可以重复,序号连续 ROW_NUMBER 排序函数 对该分组下的所有结果作一个排序,基于该分组给一个行数 PERCENT_RANK 分布函数 每行按照公式 (rank-1) / (rows-1) 进行计算 CUME_DIST 分布函数 分组内小于、等于当前 rank 值的行数 / 分组内总行数
-
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 -
ROWS BETWEEN子句常见关键字含义:
关键字 含义 PRECEDING 当前行数往前 FOLLOWING 当前行数往后 CURRENT ROW 当前行 UNBOUNDED 起点(一般结合PRECEDING,FOLLOWING使用) UNBOUNDED PRECEDING 表示该窗口最前面的行(起点) UNBOUNDED FOLLOWING 表示该窗口最后面的行(终点) -
优势:
- 保留行信息:窗口函数不会丢失行信息,允许分析每一行的上下文。
- 灵活性:可以对数据进行多维度分析,比如按时间、区域等进行分组。
- 性能:在大数据集上,使用窗口函数可以更有效地处理数据分析。