TypechoJoeTheme

Toasobi的博客

Sql窗口函数(附例题)

本文最后更新于2023年09月20日,已超过364天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!
  • 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 NameType
customer_idint
namevarchar
visited_ondate
amountint

在 SQL 中,(customer_id, visited_on) 是该表的主键。
该表包含一家餐馆的顾客交易数据。
visited_on 表示 (customer_id) 的顾客在 visited_on 那天访问了餐馆。
amount 是一个顾客某一天的消费总额。

你是餐馆的老板,现在你想分析一下可能的营业额变化增长(每天至少有一位顾客)。

计算以 7 天(某日期 + 该日期前的 6 天)为一个时间段的顾客消费平均值。average_amount 要 保留两位小数。

结果按 visited_on 升序排序。

返回结果格式的例子如下。

示例 1:

输入:

Customer 表:
customer_idnamevisited_onamount
1Jhon2019-01-01100
2Daniel2019-01-02110
3Jade2019-01-03120
4Khaled2019-01-04130
5Winston2019-01-05110
6Elvis2019-01-06140
7Anna2019-01-07150
8Maria2019-01-0880
9Jaze2019-01-09110
1Jhon2019-01-10130
3Jade2019-01-10150
输出:
visited_onamountaverage_amount
2019-01-07860122.86
2019-01-08840120
2019-01-09840120
2019-01-101000142.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。
朗读
赞(0)
评论 (0)