LeetCode_sql_day31(1384.按年度列出销售总额)

news/2024/9/23 5:02:36/

目录

描述  1384.按年度列出销售总额

数据准备

分析

法一

法二

代码

总结


描述  1384.按年度列出销售总额

 Product 表:

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| product_id    | int     |
| product_name  | varchar |
+---------------+---------+
product_id 是这张表的主键(具有唯一值的列)。
product_name 是产品的名称。

Sales 表:

+---------------------+---------+
| Column Name         | Type    |
+---------------------+---------+
| product_id          | int     |
| period_start        | date    |
| period_end          | date    |
| average_daily_sales | int     |
+---------------------+---------+
product_id 是这张表的主键(具有唯一值的列)。
period_start 和 period_end 是该产品销售期的起始日期和结束日期,且这两个日期包含在销售期内。
average_daily_sales 列存储销售期内该产品的日平均销售额。
销售日期范围为2018年到2020年。

编写解决方案,找出每个产品每年的总销售额,并包含 product_id , product_name , report_year 以及 total_amount 。

返回结果并按 product_id 和 report_year 排序

返回结果格式如下例所示。

示例 1:

输入:
Product table:
+------------+--------------+
| product_id | product_name |
+------------+--------------+
| 1          | LC Phone     |
| 2          | LC T-Shirt   |
| 3          | LC Keychain  |
+------------+--------------+
Sales table:
+------------+--------------+-------------+---------------------+
| product_id | period_start | period_end  | average_daily_sales |
+------------+--------------+-------------+---------------------+
| 1          | 2019-01-25   | 2019-02-28  | 100                 |
| 2          | 2018-12-01   | 2020-01-01  | 10                  |
| 3          | 2019-12-01   | 2020-01-31  | 1                   |
+------------+--------------+-------------+---------------------+
输出:
+------------+--------------+-------------+--------------+
| product_id | product_name | report_year | total_amount |
+------------+--------------+-------------+--------------+
| 1          | LC Phone     |    2019     | 3500         |
| 2          | LC T-Shirt   |    2018     | 310          |
| 2          | LC T-Shirt   |    2019     | 3650         |
| 2          | LC T-Shirt   |    2020     | 10           |
| 3          | LC Keychain  |    2019     | 31           |
| 3          | LC Keychain  |    2020     | 31           |
+------------+--------------+-------------+--------------+
解释:
LC Phone 在 2019-01-25 至 2019-02-28 期间销售,该产品销售时间总计35天。销售总额 35*100 = 3500。
LC T-shirt 在 2018-12-01 至 2020-01-01 期间销售,该产品在2018年、2019年、2020年的销售时间分别是31天、365天、1天,2018年、2019年、2020年的销售总额分别是31*10=310、365*10=3650、1*10=10。
LC Keychain 在 2019-12-01 至 2020-01-31 期间销售,该产品在2019年、2020年的销售时间分别是:31天、31天,2019年、2020年的销售总额分别是31*1=31、31*1=31。

数据准备

sql">Create table If Not Exists Product (product_id int, product_name varchar(30))
Create table If Not Exists Sales (product_id int, period_start date, period_end date, average_daily_sales int)
Truncate table Product
insert into Product (product_id, product_name) values ('1', 'LC Phone ')
insert into Product (product_id, product_name) values ('2', 'LC T-Shirt')
insert into Product (product_id, product_name) values ('3', 'LC Keychain')
Truncate table Sales
insert into Sales (product_id, period_start, period_end, average_daily_sales) values ('1', '2019-01-25', '2019-02-28', '100')
insert into Sales (product_id, period_start, period_end, average_daily_sales) values ('2', '2018-12-01', '2020-01-01', '10')
insert into Sales (product_id, period_start, period_end, average_daily_sales) values ('3', '2019-12-01', '2020-01-31', '1')

分析

法一

①首先用recursive循环构造出这个report_year 我是用period_start来构造 计数器是 开始年份加一 停止条件是结束年份不大于开始年份就停止 

with recursive year as (select product_id,cast(year(period_start) as char) report_year,year(period_end)                 end,average_daily_sales,period_start,period_endfrom Salesunion allselect product_id, report_year + 1, end, average_daily_sales, period_start, period_endfrom yearwhere end > report_year)
select * from year

②使用case when条件判断 每个产品在该年的销售天数

如果report_year 与开始和结束的时间年份相同 说明 该产品只在当年销售 用结束时间减去开始时间 + 1 即可

如果report_year与开始时间年份相同 小于结束时间的年份 说明该产品在下一年还在销售 此时用concat(开始时间年份,‘-12-31’) 当年最后一天的时间减去开始销售的时间 作为该年销售天数

如果report_year不等于开始时间年份 也不等于结束时间年份 说明该产品该年一整年都在销售 返回365

如果report_year大于开始时间年份 等于结束时间年份 说明 在该年销售结束 用该年结束时间减去构造的concat(结束时间年份,'-01-01') +1 即该年销售天数

select product_id,report_year,average_daily_sales,casewhen report_year = year(period_start) and report_year = year(period_end)then datediff(period_end, period_start) + 1 -- 产品只在当年卖when report_year = year(period_start) and report_year < year(period_end)then datediff(concat(report_year, '-12-31'), period_start) + 1 -- 产品销售年份与产品开始销售年份相同时when report_year > year(period_start) and report_year < year(period_end)then 365 -- 产品销售年份 跨越了多个年份when report_year > year(period_start) and report_year = year(period_end) -- 产品销售年份与产品结束销售年份相同时then datediff(period_end, concat(report_year, '-01-01')) + 1end r1from year

③最后按照题目要求求出总销售额,连接产品名称 并且排序

select t2.product_id, product_name, report_year, r1 * average_daily_sales total_amount
from t2join product on t2.product_id = product.product_id
order by product_id, report_year

 # 用cast强制转换report_year 年 是因为题目最后要求使用字符串类型

法二

①利用recursive求出最大的Sales表中的时间差

with recursive diff as (select 0 as day_diffunion allselect day_diff + 1from diffwhere day_diff < (select max(datediff(period_end, period_start)) from Sales))
select * from diff

② 用开始销售的日期加上日期差就是销售的年份 这里很巧妙的将diff循环表与Sales表连接起来 相当于标明了每一天的销售年份

with recursive diff as (select 0 as day_diffunion allselect day_diff + 1from diffwhere day_diff < (select max(datediff(period_end, period_start)) from Sales))
select sales.product_id,cast(year(date_add(period_start, interval day_diff day)) as char) as report_year, 
day_diff
from diffjoin sales on datediff(period_end, period_start) >= day_diff

③此时就可以根据产品,年份 计算该年总的销售额  同时连接产品名称 排序 cast强制转换year的类型为字符串型 原因同上

select sales.product_id,product_name,cast(year(date_add(period_start, interval day_diff day)) as char) as report_year,sum(average_daily_sales)total_amount
#         day_diff
from diffjoin sales on datediff(period_end, period_start) >= day_diffjoin product on Sales.product_id = Product.product_id
group by product_id,product_name,report_year
order by product_id, report_year

代码

sql"># 法一:
with recursive year as (select product_id,cast(year(period_start) as char) report_year,year(period_end)                 end,average_daily_sales,period_start,period_endfrom Salesunion allselect product_id, report_year + 1, end, average_daily_sales, period_start, period_endfrom yearwhere end > report_year), t2 as (select product_id,report_year,average_daily_sales,casewhen report_year = year(period_start) and report_year = year(period_end)then datediff(period_end, period_start) + 1 -- 产品只在当年卖when report_year = year(period_start) and report_year < year(period_end)then datediff(concat(report_year, '-12-31'), period_start) + 1 -- 产品销售年份与产品开始销售年份相同时when report_year > year(period_start) and report_year < year(period_end)then 365 -- 产品销售年份 跨越了多个年份when report_year > year(period_start) and report_year = year(period_end) -- 产品销售年份与产品结束销售年份相同时then datediff(period_end, concat(report_year, '-01-01')) + 1end r1from year)
select t2.product_id, product_name, report_year, r1 * average_daily_sales total_amount
from t2join product on t2.product_id = product.product_id
order by product_id, report_year;
# 法二:
with recursive diff as (select 0 as day_diffunion allselect day_diff + 1from diffwhere day_diff < (select max(datediff(period_end, period_start)) from Sales))
select sales.product_id,product_name,cast(year(date_add(period_start, interval day_diff day)) as char) as report_year,sum(average_daily_sales)                                             total_amount
#        day_diff
from diffjoin sales on datediff(period_end, period_start) >= day_diffjoin product on Sales.product_id = Product.product_id
group by product_id, product_name, report_year
order by product_id, report_year;

总结

①加深对recursive循环的理解

②对于法二所展现的思想 需要积累

先求出最大的时间差 然后用开始时间相加 获取每一天的年份  省去了复杂的条件判断


http://www.ppmy.cn/news/1529162.html

相关文章

NodeJs文档

文件操作 // 1. 导入fs模块 const fs require(fs)文件写入 //异步写入 // fs.writeFile(文件名&#xff0c; 待写入的数据&#xff0c; 选项设置&#xff08;可选&#xff09;&#xff0c; 回调函数) fs.writeFile(./座右铭.txt, 三人行&#xff0c;必有我师傅, err > {/…

MySQL函数介绍--日期与时间函数(二)

我相信大家在学习各种语言的时候或多或少听过我们函数或者方法这一类的名词&#xff0c;函数在计算机语言的使用中可以说是贯穿始终&#xff0c;那么大家有没有思考过到底函数是什么&#xff1f;函数的作用又是什么呢&#xff1f;我们为什么要使用函数&#xff1f;其实&#xf…

C++基础知识7 list

list 1. list的介绍及使用1.1 list的介绍1.2 list的使用1.2.1 list的构造1.2.2 list iterator的使用1.2.3 list capacity1.2.4 list element access1.2.5 list modifiers1.2.6 list的迭代器失效 2.1 模拟实现list 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 l…

Qt中的延时

单次触发延时 单次触发延时是指定时器在指定的延时后触发一次&#xff0c;然后自动停止。这种方式非常适合只需要延时执行一次操作的场景。 #include <QTimer> #include <QObject>class MyClass : public QObject {Q_OBJECT public:MyClass() {QTimer::singleSho…

MyBatis XML映射文件编写【后端 18】

MyBatis XML映射文件编写 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射&#xff0c;将接口和 Java 的 POJOs …

Centos7.9 使用 Kubeadm 自动化部署 K8S 集群(一个脚本)

文章目录 一、环境准备1、硬件准备&#xff08;虚拟主机&#xff09;2、操作系统版本3、硬件配置4、网络 二、注意点1、主机命名格式2、网络插件 flannel 镜像拉取2.1、主机生成公私钥2.2、为啥有 Github 还用 Gitee2.3、将主机公钥添加到 Gitee2.3.1、复制主机上的公钥2.3.2、…

2024年中国科技核心期刊目录(自然科学卷)科技统计源核心(续)

2024年中国科技核心期刊目录 &#xff08;自然科学卷&#xff09; 序号 期刊代码 期刊名称 1001 G855 临床消化病杂志 1002 Q909…

Vue3新组件transition(动画过渡)

transition组件&#xff1a;控制V-if与V-show的显示与隐藏动画 1.基本使用 <template><div><button click"falg !falg">切换</button><transition name"fade" :enter-to-class"etc"><div v-if"falg&quo…