Toasobi
Sql窗口函数(附例题)
- sum()聚合
基本案例:
patition 表示分组,但是该分组和group by有些区别,具体在文章下面会有讲解rows between
表示以行为标准计算sum(也可以使用其他的标准,下面有讲),unbounded
表示无边界的:
- UNBOUNDED PRECEDING:表示窗口的起始点没有限制,即从最开始的行开始计算。
UNBOUNDED FOLLOWING:表示窗口的结束点没有限制,即计算到最后一行。
current row
表示当前行也加入操作sum(amount_percent) over(partition by cat_name order by amount_percent desc rows between unbounded preceding and current row) as amount_percent_num
当前日期以及前六天(visited_on)的amount之和RANGE BETWEEN
表示以范围为标准计算sum,这里范围标注为:INTERVAL '6' DAY(日期) PRECEDING
INTERVAL
是用于指定相对时间间隔的关键字。它可以与RANGE或ROWS一起使用,用于定义窗口的边界,后面跟'n'
'n' DAY FOLLOWING:表示相对于当前日期的后n天
SUM(amount) OVER (ORDER BY visited_on RANGE BETWEEN INTERVAL '6' DAY PRECEDING AND CURRENT ROW) AS sum_amount
例题:
表: 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 DISTINCT visited_on,
sum_amount AS amount,
ROUND(sum_amount/7, 2) AS average_amount
-- 以下是解决前六天包括当前的总和(有可能某一天不存在客人)
FROM (
SELECT visited_on,
SUM(amount) OVER (ORDER BY visited_on RANGE BETWEEN INTERVAL '6' DAY PRECEDING AND CURRENT ROW) AS sum_amount
-- 以下是计算每天的金额总量
FROM (
SELECT visited_on,
SUM(amount) AS amount
FROM Customer
GROUP BY visited_on
) a
) b
-- 最后手动只要覆盖完整7天的数据
WHERE DATEDIFF(visited_on, (SELECT MIN(visited_on) FROM Customer)) >= 6
- 专用窗口函数rank
假设一段代码:
select *,
rank() over (partition by 班级
order by 成绩 desc) as ranking
from 班级表
注意:group by分组汇总后改变了表的行数,一行只有一个类别。而partiition by和rank函数不会减少原表中的行数
- 其他窗口函数
举个例子:
select *,
rank() over (order by 成绩 desc) as ranking,
dense_rank() over (order by 成绩 desc) as dese_rank,
row_number() over (order by 成绩 desc) as row_num
from 班级表
从上面的结果可以看出:
rank函数:这个例子中是5位,5位,5位,8位,也就是如果有并列名次的行,会占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1,4。
dense_rank函数:这个例子中是5位,5位,5位,6位,也就是如果有并列名次的行,不占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1,2。
row_number函数:这个例子中是5位,6位,7位,8位,也就是不考虑并列名次的情况。比如前3名是并列的名次,排名是正常的1,2,3,4。