SQL.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

这道题的难点在于我们如何基于当天拿到前6天的数据,以来求取这七天的平均数。

而且拿到了这样的数据我们还得想怎么去除不满足前面有6天的数据,例如示例的2019-01-01,它前面都没有六天的数据,所以就没有必要展示。

我们知道一定的是基于七天的一个度量去进行计算,所以我们可以联想到一个窗口,窗口的大小固定为7,基于当天我们这个窗口包含住前6天的数据进行计算来得到结果。

我们知道MySQL是有窗口函数的,但是其实窗口函数是有定长的窗口的功能。

语法为:

sql 复制代码
<window_function> OVER (
  [PARTITION BY partition_column]
  [ORDER BY order_column]
  [ROWS or RANGE BETWEEN <frame_start> AND <frame_end>]
)

后面的ROWS与RANGE就是用来制定窗口的大小的。

详情可以查看我上一篇定长窗口的SQL

https://blog.csdn.net/m0_65013257/article/details/142170042?spm=1001.2014.3001.5501

现在我们已经有了计算固定窗口大小的函数,但我们还得需要一个细节。每一天是有多条记录的,也就是会有多个顾客购买,我们应该是基于当天的总额来进行窗口的计算。所以我们首先得算出每一天的总额

sql 复制代码
select visited_on,sum(amount) amount
from customer
group by visited_on

现在我们可以基于这样一个基础表进行计算。需要运用两次窗口函数。

一次窗口计算七天内的总和。一次窗口计算七天内的平均数。

写出对应的SQL

sql 复制代码
select visited_on,
sum(amount) over (order by visited_on rows 6 preceding) amount,
avg(t1.amount) over (order by visited_on rows 6 preceding) as average_amount
from (
    select visited_on,sum(amount) amount
    from customer
    group by visited_on
) t1

这里还有一点瑕疵,平均数题目的示例中需要保留两位小数。所以我们还要使用Round函数

sql 复制代码
select visited_on,
sum(amount) over (order by visited_on rows 6 preceding) amount,
round(avg(t1.amount) over (order by visited_on rows 6 preceding),2) as average_amount
from (
    select visited_on,sum(amount) amount
    from customer
    group by visited_on
) t1

现在我们就算出每一天的七天内的平均额度与总额度了。但是我们还得需要去除未能有前面6天数据的数据。那这样我们应该有两种想法。一种应该是跳过,还有一种应该是基于一种判定,我们只需要判定后的数据。

我这里先阐述跳过的方法。我们可以跳过前六天的数据,然后获取后面所有的数据即可,但是遇到一个麻烦的事,MySQL当中没有这样的语法说获取到后面所有的数据。但我们可以拟一个较大的数,这样就可以满足我们的需求。

完整的SQL如下

sql 复制代码
select visited_on,
sum(amount) over (order by visited_on rows 6 preceding) amount,
round(avg(t1.amount) over (order by visited_on rows 6 preceding),2) as average_amount
from (
    select visited_on,sum(amount) amount
    from customer
    group by visited_on
) t1
limit 100000000000000 offset 6

那我们如何基于第二种想法解题呢,或者说还有其他方法吗?

实际上我们可以减少一次窗口函数的使用,因为有了七天的总额度,我们可以直接除以7就可以得倒平均数。而除去前六天的数据可以已时间的差值来进行判定,拿到所有时间中的最小值,然后当天的时间剪去最小值的时间如果大于等于6就行。

我们基础表不变,还是需要每一天的总额。

sql 复制代码
select visited_on,sum(amount) amount
        from customer
        group by visited_on
        ) t1

然后运用一次窗口计算总额

sql 复制代码
select visited_on,
    sum(amount) over (order by visited_on rows 6 preceding) amount
    from (
        select visited_on,sum(amount) amount
        from customer
        group by visited_on
        ) t1

然后筛选时间的SQL语句为

sql 复制代码
where datediff(visited_on,(select min(visited_on) from customer)) >= 6

然后算出平均数即可。总SQL为

sql 复制代码
select visited_on,
amount,
round(amount / 7,2) as average_amount
from
(
    select visited_on,
    sum(amount) over (order by visited_on rows 6 preceding) amount
    from (
        select visited_on,sum(amount) amount
        from customer
        group by visited_on
        ) t1
) t2
where datediff(visited_on,(select min(visited_on) from customer)) >= 6
相关推荐
OpsEye6 分钟前
MySQL 8.0.40版本自动升级异常的预警提示
数据库·mysql·数据库升级
Ljw...6 分钟前
表的增删改查(MySQL)
数据库·后端·mysql·表的增删查改
王燕龙(大卫)17 分钟前
leetcode 数组中第k个最大元素
算法·leetcode
远歌已逝3 小时前
维护在线重做日志(二)
数据库·oracle
qq_433099404 小时前
Ubuntu20.04从零安装IsaacSim/IsaacLab
数据库
Dlwyz4 小时前
redis-击穿、穿透、雪崩
数据库·redis·缓存
工业甲酰苯胺6 小时前
Redis性能优化的18招
数据库·redis·性能优化
没书读了7 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
i道i8 小时前
MySQL win安装 和 pymysql使用示例
数据库·mysql
小怪兽ysl8 小时前
【PostgreSQL使用pg_filedump工具解析数据文件以恢复数据】
数据库·postgresql