HQL专栏之初级练习2

系列文章目录

第一章 HQL表创建
第二章 HQL初级练习1

前言

这里HQL的初级练习,用于复习sql常用的函数等使用方法.

一、习题练习

8.根据商品销售情况进行商品分类

  1. 需求:销售件数0-5000为冷门商品,5001-19999为一般商品,20000以上为热门商品,统计不同类别商品的数量
  2. 分析表:需要知道商品和销售量,考虑订单详情表(order_detail).
  3. 分析题意:根据商品进行分组,用于求出某件商品的总销售数量,然后按照不同的数量进行分类.

表结构
订单明细id-------------------订单id--------------商品id---------下单时间-------------下单时的价格----下单商品件数
在这里插入图片描述

  1. 使用的函数
  1. 条件判断函数
    case A when B1 then C1 [ when B2 then C2] else D end
    如果A=B1则返回C1,如果A=B2则返回C2,否则返回D.
    case when A then A1 when B then B1 else C end
    如果A判断后为Ture则返回A1,如果B判断后为Ture则返回B1,否则返回C.

代码演示

select t1.sku_type,count(*) count_num
from (
         select case when sum( sku_num ) > = 0 and sum( sku_num ) <= 5000    then '冷门商品'
                     when sum( sku_num ) > 5000 and sum( sku_num ) <= 19999 then '一般商品'
                     when sum( sku_num ) > 20000                            then '热门商品' end sku_type
         from order_detail
         group by sku_id
     )t1
group by t1.sku_type;

结果
在这里插入图片描述

9.查询有新增用户的日期的新增用户数和新增用户一日留存率

  1. 需求: 留存率=第一天登录后第二天也登录的数量/这第一天新增的用户数量
  2. 分析表: 需要知道用户是否为新增,只需要看这个用户是否有登录(user_login_detail)
  3. 分析题意:方式一可以按照时间来求,方式二分别求出某天的新增量和这一天的留存量,然后关联求出最终结果.

表结构(user_login_detail):
用户id---------------------用户登录ip地址------------登录时间-----------------------登出时间在这里插入图片描述

  1. 涉及函数
  1. to_date(): 格式化时间,例子to_date(‘2011-12-08 10:03:01’)=2011-12-08
    date_format( , ):格式化时间date_format(“2016-06-22”,“yy-MM-dd”)=2016-06-22
  2. lag(A, B ,C)over(partition by … order by …): 偏移量用于查找当前行的向上偏移.(里面参数根据需求个人书写), A需要偏移的字段,B偏移量,C当前值没有偏移值,书写的替代值.
    lead(A, B ,C)over(partition by … order by …): 偏移量用于查找当前行的向下偏移,(其他同上)
  3. datediff(A,B): 计算两个日期的差值.
  4. rank() over (partition by … order by… ): 排序函数:排名相同时会重复,总数不会减少(1,2,2,2,5……)。
    dense_rank() over(……): 排名相同时会重复,总数会减少(1,2,2,2,3……)
    row_number() over(……) :行号(1,2,3,4,5,6,7……)

代码演示

--第一步:求每一个用户最开始登录的时间(注册时间),这里也可以使用排名=1的方式(如步骤2)得出.
select user_id, to_date(min(login_ts)) from user_login_detail group by user_id;
--第二步:得出当前时间下一个时间,这里可以根据时间差得出是否是第一天登录了第二天也登录的用户.
select t1.user_id,
       t1.td,
       lead( t1.td , 1 , t1.td ) over (partition by t1.user_id order by t1.td) second_login,
       rank( ) over (partition by t1.user_id order by t1.td)                   rank_login
from (
         select distinct user_id, to_date( login_ts ) td
         from user_login_detail
     ) t1;

--第三步:过滤出排名=1.
select t2.user_id,
       t2.td,
       t2.second_login
from (
         select t1.user_id,
                t1.td,
                lead( t1.td , 1 , t1.td ) over (partition by t1.user_id order by t1.td) second_login,
                rank( ) over (partition by t1.user_id order by t1.td)                   rank_login
         from (
                  select distinct user_id, to_date( login_ts ) td
                  from user_login_detail
              ) t1
     )t2 where t2.rank_login=1;

--第四步:对时间进行分组,分别计算出每天新增数量,第二天留存数量
select t3.td,
       count(*),
       count(`if`(datediff(t3.second_login,t3.td)=1,1,null))/count(*)
from (
       select t2.user_id,
       t2.td,
       t2.second_login
from (
         select t1.user_id,
                t1.td,
                lead( t1.td , 1 , t1.td ) over (partition by t1.user_id order by t1.td) second_login,
                rank( ) over (partition by t1.user_id order by t1.td)                   rank_login
         from (
                  select distinct user_id, to_date( login_ts ) td
                  from user_login_detail
              ) t1
     )t2 where t2.rank_login=1
     ) t3
group by t3.td;

------这里展示方式二,分别计算两者数量然后进行关联
select nvl(tt1.login,tt2.login),   --nvl():null值替换
       nvl(tt1.count_first_login,0),
       nvl(tt2.count_second_login,0),
       nvl(tt2.count_second_login,0)/nvl(tt1.count_first_login,0)
from (
         select t2.login,
                count( * ) count_first_login
         from (
                  select t1.user_id,
                         t1.login,
                         rank( ) over (partition by t1.user_id order by t1.login) rank_login
                  from (
                           select distinct user_id, to_date( login_ts ) login
                           from user_login_detail
                       ) t1
              ) t2
         where t2.rank_login = 1
         group by t2.login
     ) tt1
      left  join (  ---左关联(留存的用户数量<=新增的用户数量)
    select t2.login,
           count( * ) count_second_login
    from (
             select t1.user_id,
                    t1.login,
                    lead( t1.login , 1 , t1.login ) over (partition by t1.user_id order by t1.login) second_login,
                    rank( ) over (partition by t1.user_id order by t1.login)                         rank_login
             from (
                      select distinct user_id, to_date( login_ts ) login
                      from user_login_detail
                  ) t1
         ) t2
    where t2.rank_login = 1
      and datediff( t2.second_login , t2.login ) = 1
    group by t2.login
)tt2 on tt1.login=tt2.login;

在这里插入图片描述

10.登录次数及交易次数统计

  1. 分析表: 登录次数(user_login_detail), 交易次数(这里就是已经形成订单的表:配送表(delivery_info))
  2. 分析题意: 可以分别计算登录次数和交易次数,按照相同用户id和时间进行分组,最后两个表进行关联.

表结构(delivery_info)
配送id,-----------------------订单id---------------用户id----------------下单时间---------------预计配送时间在这里插入图片描述

  1. 所需函数介绍:
  1. nvl(A,B): 用于将null值进行替换的函数,当A为null时,使用B值进行替换.
  2. to_date: 格式化时间

代码演示

--第一步:统计登录次数(每一天),对用户和登录时间进行分组,统计登录的次数
select user_id,to_date(login_ts),count(*) from user_login_detail
group by user_id,to_date(login_ts);
--第二步: 统计用户交易的次数(有下单),
select user_id,order_date,count(*) from delivery_info
group by user_id,order_date;

--第三步:将两个表进行left join操作,关联条件是用户和时间,要保证一定是登录了,才会有下单
select t1.user_id,
       t1.td,
       t1.count_login,
       nvl(t2.count_delivery,0)
from (
         select user_id, to_date( login_ts ) td, count( * ) count_login
         from user_login_detail
         group by user_id, to_date( login_ts )
     ) t1
     left join (
    select user_id, order_date, count( * ) count_delivery
    from delivery_info
    group by user_id, order_date
) t2
on t1.user_id=t2.user_id and t1.td=t2.order_date;

这个数据有点多,这里没有展示完全.(有28行)
在这里插入图片描述

11.某周内每件商品每天销售情况

  1. 需求: 查询2021年9月27号-2021年10月3号这一周所有商品每天销售件数.
  2. 分析表: 要的是商品的销售总的数量,订单详情表(order_detail),满足有时间有数量,有商品id.
  3. 分析题意: 对商品id和时间进行分组,从而计算出不同商品在同一天销售的总数量.然后条件判断计算在这一星期每一天销售的数量.

表结构(order_detail):
订单明细id-------------------订单id--------------商品id----------下单时间--------------下单时的价格-----下单数量

  1. 使用的函数介绍:
  1. dayofweek(date): 可以用于查看某一天是星期几,
    ----------------------星期一 ,星期二, 星期三, 星期四, 星期五, 星期六, 星期日
    dayofweek(date): 2 ==== 3 ==== 4 ==== 5 ==== 6 ==== 7 ==== 1
    这里的数字对应上述的星期.
    但是hive自身关于这个函数有个bug存在,这个函数单独使用的时候就如同上述,但是当结合sum(dayofweek())的时候和上述不同,和正常的星期数一样..

经过测试和查询:

  1. 当再hive中使用dayofweek()的时候,如果要和正常函数用法相同的话,需要将时间进行数据类型转化,如果原时间类型为date则不需要考虑转化,如果为string类型,可以使用cast(‘时间’ as date)进行数据转化.
  2. 也可以不进行转化,直接按照正常的星期数字进行书写.(如下展示)

代码演示

--出现这个问题是sum()聚合,导致的,如果没有sum()函数可以正常使用.
--第一步:
select sku_id,
      `dayofweek`( cast(create_date as date) ) flag,   --这里就不用再考虑hive这个bug的问题,按照正常函数处理
       sku_num
from order_detail
where create_date between '2021-09-27' and '2021-10-03';
--第二步(1): 子查询
select t1.sku_id,
       sum(`if`(t1.flag=2,t1.sku_num,0)) monday,
       sum(`if`(t1.flag=3,t1.sku_num,0)) tuesday,
       sum(`if`(t1.flag=4,t1.sku_num,0)) wednesday,
       sum(`if`(t1.flag=5,t1.sku_num,0)) thursday,
       sum(`if`(t1.flag=6,t1.sku_num,0)) friday,
       sum(`if`(t1.flag=7,t1.sku_num,0)) saturday,
       sum(`if`(t1.flag=1,t1.sku_num,0)) sunday
from (
         select sku_id,
                create_date,
                `dayofweek`(cast( create_date as date)) flag,
                sku_num
         from order_detail
         where create_date >= '2021-09-27' and create_date <= '2021-10-03'
     )t1
group by t1.sku_id;
--第二步(2): 不使用cast
select t1.sku_id,
       sum(`if`(t1.flag=1,t1.sku_num,0)) monday,
       sum(`if`(t1.flag=2,t1.sku_num,0)) tuesday,
       sum(`if`(t1.flag=3,t1.sku_num,0)) wednesday,
       sum(`if`(t1.flag=4,t1.sku_num,0)) thursday,
       sum(`if`(t1.flag=5,t1.sku_num,0)) friday,
       sum(`if`(t1.flag=6,t1.sku_num,0)) saturday,
       sum(`if`(t1.flag=7,t1.sku_num,0)) sunday
from (
         select sku_id,
                create_date,
                `dayofweek`(create_date) flag,
                sku_num
         from order_detail
         where create_date >= '2021-09-27' and create_date <= '2021-10-03'
     )t1
group by t1.sku_id;

在这里插入图片描述

12.同期商品售卖分析表

  1. 需求:统计同一个商品在2021年和2022年中同一个月的销量对比.
  2. 分析表: 时间,销量,商品(order_detail)
  3. 个人当时错误的思路: 当时写的时候分别计算了这两年月份和销售总量,然后将这两个表join操作(按照商品的id和月份), 最后发现少考虑一种情况: 要求的是20年21年同月的销售量,就有可能出现20年在某个月卖了A商品没有卖B商品,但是21年相同的月卖了B商品但是没有卖A商品的情况
    如果只join,这里只求出了在20年和21年相同月份卖的相同商品(此时的商品只会有20年和21年都有销量的商品,并不是所有商品种类).因此注意这个情况.(但这里也可以使用full join计算出结果)

代码演示

--第一步: 计算21年的商品,在不同月份的销售量
select sku_id,month(create_date),sum(sku_num) from order_detail
where year(create_date)=2020
group by sku_id,month(create_date);

--第二步:计算22年商品,在不同的月份的销售量.
select sku_id,month(create_date),sum(sku_num) from order_detail
where year(create_date)=2021
group by sku_id,month(create_date);
--第三步:将上述的两个表进行full join
select nvl(t1.sku_id,t2.sku_id) sku_id,
       nvl(t1.20_month,t2.21_month),
       nvl(t1.20_skunum,0),
       nvl(t2.21_skunum,0)
from (
         select sku_id, month( create_date ) 20_month, sum( sku_num ) 20_skunum
         from order_detail
         where year( create_date ) = 2020
         group by sku_id, month( create_date )
     ) t1 full join (
         select sku_id, month( create_date ) 21_month, sum( sku_num ) 21_skunum
         from order_detail
         where year( create_date ) = 2021
         group by sku_id, month( create_date )
)t2 on t1.sku_id=t2.sku_id and t1.20_month=t2.21_month
order by sku_id;

----------上述的方法,过于繁琐.
--可是使用如下方法,使用sum(),if()函数结合的方式.
select sku_id,
       month(create_date),
       sum(`if`(year(create_date)=2020,sku_num,0)) 20_skunum,
       sum(`if`(year(create_date)=2021,sku_num,0)) 21_skunum
from order_detail
where year(create_date)=2020 or year(create_date)=2021
group by sku_id, month(create_date)
order by sku_id;

部分结果展示
在这里插入图片描述

13.国庆期间每个sku的收藏量和购买量

  1. 需求:求2021年国庆节期间(10月1日-10月7日),每个商品的购买总数量和总收藏次数.
  2. 分析表: 收藏,商品,时间:(favor_info), 购买=销量,商品,时间(order_detail)
  3. 分析题意: 分别计算收藏量和购买量,最后根据商品的id进行关联, 注意还有时间范围在国庆期间(10-01~10-07)

表结构展示(favor_info):
用户id---------------------------------商品id----------------------收藏日期
在这里插入图片描述

代码演示

--第一步: 计算销售量
select sku_id, sum( sku_num ) sku_sum
from order_detail
where month( create_date ) = 10
  and day( create_date ) between 1 and 7
group by sku_id;

--第二步: 同上计算收藏量
select sku_id,count(sku_id) from favor_info
where month(create_date)=10 and
      day(create_date) between 1 and 7
group by sku_id;

--第三步: 上述两个表join
select nvl(t1.sku_id,t2.sku_id),
       nvl(t1.sku_sum,0),
       nvl(t2.sku_sum,0)
from (
         select sku_id, sum( sku_num ) sku_sum
         from order_detail
         where month( create_date ) = 10
           and day( create_date ) between 1 and 7
         group by sku_id
     ) t1 full join (
         select sku_id, count( sku_id ) sku_sum
         from favor_info
         where month( create_date ) = 10
            and day( create_date ) between 1 and 7
         group by sku_id
)t2 on t1.sku_id=t2.sku_id;

计算结果:
在这里插入图片描述

14.21年国庆节期间各品类商品的7日动销率和滞销率

  1. 需求分析:
    –动销率:有销量的商品种类数/已上架总商品种类数
    –滞销率:没有销量的商品种类数/已上架总商品种类数
    –解释: 同一类商品有的有销量,有的没有, 所以有销量的商品种类/总上架的商品种类= 动销率 ,总上架的可以有sku_info得出,
  2. 分析表: 各品类商品(sku_info), 销量的商品(order_detail)

表结构(sku_info)
商品id-------------------商品名称---------商品品类id--------------------商品上架日期--------------商品上架时的价格
在这里插入图片描述

  1. 函数使用说明:

1.cast(A as B ): 将A数据类型转化为B类型
例如: cast(‘2021-10-10’ as date): 就是将字符类型的时间转化为date类型
2. 还有一种
cast(A as decimal(16,2)): 本质还是数据类型转化,这里A数据会保留2位小数,数总位数(整数位14,小数位2)为:16.
代码演示

--第一步: 先过滤在国庆期间,并进行去重,得到有销量的商品id
select distinct sku_id from order_detail
where create_date between '2021-10-01' and '2021-10-07';
select sku.category_id,
       --解释: 先进行时间的确定,if判断语句会记录这里的商品id,最后利用去重+count得到在一个种类中的商品销量的情况,  第二个时间需要先判断上架时间要小于这一天的时间.
       cast(count(distinct `if`(t1.create_date='2021-10-01',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-01',sku.sku_id,null))as decimal(16,2)),
       cast(count(distinct `if`(t1.create_date='2021-10-02',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-02',sku.sku_id,null))as decimal(16,2)),
       cast(count(distinct `if`(t1.create_date='2021-10-03',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-03',sku.sku_id,null))as decimal(16,2)),
       cast(count(distinct `if`(t1.create_date='2021-10-04',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-04',sku.sku_id,null))as decimal(16,2)),
       cast(count(distinct `if`(t1.create_date='2021-10-05',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-05',sku.sku_id,null))as decimal(16,2)),
       cast(count(distinct `if`(t1.create_date='2021-10-06',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-06',xiaosku.sku_id,null))as decimal(16,2)),
       cast(count(distinct `if`(t1.create_date='2021-10-07',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-07',sku.sku_id,null))as decimal(16,2))
from sku_info sku
         left join (
    select sku_id,create_date
    from order_detail
    where create_date between '2021-10-01' and '2021-10-07'
)t1 on sku.sku_id=t1.sku_id
group by sku.category_id; ---对商品的种类进行分组

---这里只写了动销率:
--滞销率= (1-动销率) 可以直接在上述表添加相应的字段.
--(1-(cast(count(distinct `if`(t1.create_date='2021-10-01',t1.sku_id,null))/count(distinct `if`(sku.from_date<='2021-10-01',sku.sku_id,null))as decimal(16,2))))

在这里插入图片描述

总结

  1. 注重分析题意,不要遗漏题目中的条件,还有怎样将需求进行转化为可以计算的方式.
  2. 还有一些逻辑的转化(新增用户,就是第一次登录的用户).
  3. 双向表转单项表更容易解决问题(向用户推荐朋友收藏的商品).
  4. 最后,关于dayofweek()的bug问题,当时思考了许久.
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值