HQL专栏之中级练习01

系列文章目录

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

前言

HQL的中级练习,对于sql函数的使用更加全面.

二、习题练习

1. 查询累积销量排名第二的商品

  1. 需求: 找到所有销售的商品中,排名第二的商品.
  2. 分析表: 需要有商品,有销量 (order_detail)
  3. 分析题意: 对商品进行分组,然后对商品的销量进行求和, 然后对这个表按照销量进行排序操作.
  4. 表结构说明:

订单明细表(order_detail)
订单明细id ----------------------订单id-------------商品id --------下单时间-------------价格------------下单数量在这里插入图片描述

  1. 函数介绍:
  1. 跳跃排序函数:rank : 排名相同时会重复,总数不会减少(1,2,2,2,5……)。
  2. 不跳跃排序函数:dense_rank: 排名相同时会重复,总数会减少(1,2,2,2,3……)。
  3. 顺序唯一的排序函数:row_number :行号(1,2,3,4,5,6,7……)。

代码演示

--第一步: 对商品进行分组,求出商品的下单总和
select sku_id,
       sum(sku_num) sku_sum
from order_detail
group by sku_id;
--第二步: 对上述的数据加上一个不跳跃的排序(可能会出现多个排名相同的商品)
select t1.sku_id,
       t1.sku_sum,
       dense_rank() over (order by t1.num desc ) dr   --dr:排名
from (
         select sku_id,
                sum( sku_num ) sku_sum
         from order_detail
         group by sku_id
     )t1;

--第三步: 进行过滤,过滤出dr=2的就是排名第二的商品
select *
from (
         select t1.sku_id,
                t1.sku_sum,
                dense_rank( ) over (order by t1.num desc ) dr
         from (
                  select sku_id,
                         sum( sku_num ) sku_sum
                  from order_detail
                  group by sku_id
              ) t1
     )t2 where t2.dr=2;
---拓展: 如果想要在没有查到这个排名的时候返回null, 可以join (select 1)
select *
from (
         select *
         from (
                  select t1.sku_id,
                         t1.num,
                         dense_rank( ) over (order by t1.num desc ) dr
                  from (
                           select sku_id,
                                  sum( sku_num ) num
                           from order_detail
                           group by sku_id
                       ) t1
              ) t2 where t2.dr=22
    )t3 right join (select 1)t4 on 1=1 ; ---右连接,左边有值的时候,可以显示出,没有值的时候返回null,(确定需要那一方输出null)

结果展示
在这里插入图片描述
拓展当没有这个排名的时候返回的是null
在这里插入图片描述

2. 查询至少连续三天下单的用户

  1. 分析表: 用户, 下单 订单详情表(order_info)
  2. 分析题意: 连续三天下单,可以使用等差数列(解释如下)的方式处理,
    等差数列:只需要判断红色时间数量,就可以判断是否是连续的三天.
    在这里插入图片描述
  3. 表结构

订单信息表(order_info)
订单id---------------------------用户id--------------------下单时间-----------------------下单总金额在这里插入图片描述

  1. 使用函数介绍:
  1. date_sub(A,B): 日期减少函数(A-B)

代码演示

--第一步:
select distinct user_id,create_date from order_info
group by user_id,create_date;
--第二步:排序并得出等差数列
select t1.user_id,
       t1.create_date,
       --进行排名使用rank
       rank() over (partition by t1.user_id order by t1.create_date) rk,
       ---时间减去后面排序的数
       date_sub(t1.create_date,rank() over (partition by t1.user_id order by t1.create_date)) drk
from (
        select user_id,create_date from order_info
    group by user_id,create_date
                  )t1;
--第三步:当进行完上述的方法后,这里就可以计算drk相同的数量,在同一id下, 但由于要保留时间,所以使用窗口函数进行计算
select t2.userid,
       t2.createdate,
       count( * ) over (partition by t2.userid,t2.drk) count_drk
from (
         select t1.user_id userid,
                t1.create_date createdate,
                --进行排名使用rank
                --rank( ) over (partition by t1.user_id order by t1.create_date)                              rk,
                ---时间减去后面排序的数
                date_sub( t1.create_date , rank( ) over (partition by t1.user_id order by t1.create_date) ) drk
         from (
                  select user_id, create_date
                  from order_info
                  group by user_id, create_date
              ) t1
     ) t2;
---第四步:再次过滤上述的count_drk>=3
select t3.userid,t3.createdate
from (
     select t2.userid,
       t2.createdate,
       count( * ) over (partition by t2.userid,t2.drk) count_drk
from (
         select t1.user_id userid,
                t1.create_date createdate,
                --进行排名使用rank
                --rank( ) over (partition by t1.user_id order by t1.create_date)                              rk,
                ---时间减去后面排序的数
                date_sub( t1.create_date , rank( ) over (partition by t1.user_id order by t1.create_date) ) drk
         from (
                  select user_id, create_date
                  from order_info
                  group by user_id, create_date
              ) t1
     ) t2
         )t3 where t3.count_drk>=3;

---方式二:
select distinct user_id
from (
         select user_id
         from (
                  select user_id,
                         create_date,
                         date_sub(create_date, row_number() over (partition by user_id order by create_date)) flag
                  from (
                           select user_id
                                , create_date
                           from order_info
                           group by user_id, create_date
                       ) t1 -- 同一天可能多个用户下单,进行去重
              ) t2 -- 判断一串日期是否连续:若连续,用这个日期减去它的排名,会得到一个相同的结果
         group by user_id, flag
         having count(flag) >= 3 -- 连续下单大于等于三天
     ) t3;

这个运行可能会出现一个错误(测试使用的是hive + spark)
在这里插入图片描述
解决方法: 在DataGrip上执行: set hive.vectorized.execution.enabled = false;


结果展示:(这两种方法user_id是相同的)
在这里插入图片描述

3. 查询各品类销售商品的种类数及销量最高的商品

  1. 需求: 要找到每一个品类中商品销量最高的商品.
  2. 分析表: 品类(sku_info), 商品种类, 商品销量 (order_detail),品类名称(Category_info)
  3. 分析题意:
    (1)可以先计算出每个商品的销售总和,然后和品类表关联,在根据品类进行分组,找到销量最高的商品.
    (2)也可以直接先将两个表进行关联,然后再分组计算.
  4. 表结构

商品信息表(sku_info)
商品id------------------ 商品名称---------- 商品所属种类id------------ 上架时间------------------ 上架时的价格
在这里插入图片描述

商品分类信息(category_info)
商品分类id-------------------------------------------------------商品分类名称
在这里插入图片描述

代码演示

--第一步:先按照商品的id进行分组,找出每件商品的销售数量.t1
select sku_id,sum(sku_num) sku_num_all from order_detail
group by sku_id;

--第二步:将上述表和商品表进行关联,商品表关联: t1 right join sku_info ,并进行商品分类分组,求出最大商品销量  ->t2
select sku.sku_id,
       sku.name,
       sku.category_id,
       nvl(t1.sku_num_all,0) num_sku,
       max( nvl( t1.sku_num_all , 0 ) ) over (partition by category_id) max_num
from sku_info sku
         left join (
    select sku_id, sum( sku_num ) sku_num_all
    from order_detail
    group by sku_id
)t1 on t1.sku_id=sku.sku_id;
--然后做一个子查询,找到每个品类最大的销售量
select t2.sku_id,
       t2.name,
       t2.category_id,
       t2.num_sku
from (
         select sku.sku_id,
                sku.name,
                sku.category_id,
                nvl( t1.sku_num_all , 0 )                                        num_sku,
                max( nvl( t1.sku_num_all , 0 ) ) over (partition by category_id) max_num   ---最大的销量数量
         from sku_info sku
                  left join (
             select sku_id, sum( sku_num ) sku_num_all
             from order_detail
             group by sku_id
         ) t1 on t1.sku_id = sku.sku_id
     )t2 where t2.num_sku=t2.max_num;  ---这里按照销售的数量查询=最大的销售数量

---第三步: 上述表和商品分类表关联: t2 right join category_info(得到品类名称)
select ci.category_id,
       ci.category_name,
       t3.sku_id,
       t3.name,
       t3.num_sku
from category_info ci
         left join (
    select t2.sku_id,
           t2.name,
           t2.category_id,
           t2.num_sku
    from (
             select sku.sku_id,
                    sku.name,
                    sku.category_id,
                    nvl( t1.sku_num_all , 0 )                                        num_sku,
                    max( nvl( t1.sku_num_all , 0 ) ) over (partition by category_id) max_num
             from sku_info sku
                      left join (
                 select sku_id, sum( sku_num ) sku_num_all
                 from order_detail
                 group by sku_id
             ) t1 on t1.sku_id = sku.sku_id
         ) t2
    where t2.num_sku = t2.max_num
) t3 on t3.category_id = ci.category_id;

-----方式二: 这里再求出每个商品数量后,再关联商品表和分类表,使用窗口函数排序得到最大销量.
select category_id,
       category_name,
       sku_id,
       name,
       order_num,
       sku_cnt
from (
         select od.sku_id,
                sku.name,
                sku.category_id,
                cate.category_name,
                order_num,
                rank() over (partition by sku.category_id order by order_num desc) rk,
                count(*) over (partition by sku.category_id)      sku_cnt
         from (
                  select sku_id,
                         sum(sku_num) order_num
                  from order_detail
                  group by sku_id
              ) od
                  join
              sku_info sku
              on od.sku_id = sku.sku_id
                  join
              category_info cate
              on sku.category_id = cate.category_id
     ) t1
where rk = 1;

结果如下: (方式二的方法:多了一个每个品类有多少商品的字段)
在这里插入图片描述

4. 查询用户的累计消费金额及VIP等级

  1. 需求: 统计每个用户截止其每个下单日期的累积消费金额,以及每个用户在其每个下单日期的VIP等级。
    若0=<X<10000,则vip等级为普通会员
    若10000<=X<30000,则vip等级为青铜会员
    若30000<=X<50000,则vip等级为白银会员
    若50000<=X<80000,则vip为黄金会员
    若80000<=X<100000,则vip等级为白金会员
    若X>=100000,则vip等级为钻石会员
  2. 表分析: 用户,下单, 下单金额(order_info)~表结构如题2
  3. 题意分析: 每个用户,每个下单日期(需要进行分组), 有可能再同一天下单了多次,这里就需要对消费的金额进行求和. 然后使用窗口函数sum()over(),可以求出每个下单日期前的下单金额,再然后做一个标记如上条件.
  4. 使用函数介绍:
  1. sum() over (partition by A order by B rows between unbounded preceding and current row)
    –求第一行到当前行的总共销售金额–unbounded preceding:第一行, current:当前行
  2. case when 条件判断1 then 结果1 when 条件判断2 then 结果2 end

代码演示

---第一步:有可能一天下单了多次,所以先分组,求出同一天的销售额
select user_id,
       create_date,
       sum(total_amount)
from order_info
group by user_id, create_date;
--第二步: 使用窗口函数统计每一次下单前的销售额总数
select t1.user_id,
       t1.create_date,
       t1.sum_amount,
       sum( t1.sum_amount )
            over (partition by t1.user_id order by t1.create_date rows between unbounded preceding and current row ) sum_total
from (
         select user_id,
                create_date,
                sum( total_amount ) sum_amount
         from order_info
         group by user_id, create_date
     ) t1;
--第三步: 
select t2.user_id,
       t2.create_date,
       t2.sum_total,
       case when t2.sum_total < 10000   then "普通会员"
            when t2.sum_total < 30000   then "青铜会员"
            when t2.sum_total < 50000   then "白银会员"
            when t2.sum_total < 80000   then "黄金会员"
            when t2.sum_total < 100000  then "白金会员"
            when t2.sum_total >= 100000 then "钻石会员" end
from (
         select t1.user_id,
                t1.create_date,
                t1.sum_amount,
                sum( t1.sum_amount )
                     over (partition by t1.user_id order by t1.create_date rows between unbounded preceding and current row ) sum_total
         from (
                  select user_id,
                         create_date,
                         sum( total_amount ) sum_amount
                  from order_info
                  group by user_id, create_date
              ) t1
     ) t2;

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

5. 查询首次下单后第二天连续下单的用户比率

  1. 需求:查询首次下单后第二天仍然下单的用户占所有下单用户的比例.
  2. 分析表: 用户,下单 ,第二天连续下单 (order_info)
  3. 分析题意: 先对用户和下单时间进行分组,用于去除(同一天下单多次的数据),然后对这个数据进行按照用户分区,时间排序进行排名,然后找到排名1,2的,进而使用窗口函数(也可以使用max,min)来对两个时间进行做差判断是否是连续的两天,在统计总人数和连续两天下单的人数,进而求出%比.
  4. 函数介绍:
  1. rank()over(): 排序.
  2. lead() over(): 找到当前行下面的数据.(根据自己指定的进行查找).
  3. round(double a, int d):四舍五入,返回保留d位小数的近似值,四舍五入只考虑d+1小数位.
  4. concat( ,% ): 用于拼接%号.

代码演示

--第一步:先对用户数据进行去重操作,对同一天下单的数据.
select user_id,create_date from order_info
group by user_id,create_date;

--第二步:使用rank()over(partation by ... )排序
select t1.user_id,
       t1.create_date,
       rank() over (partition by t1.user_id order by t1.create_date) ran
from (
         select user_id, create_date
         from order_info
         group by user_id, create_date
     )t1;
--第三步:这里需求是首次下单,第二天也下单,所以根据上述排名只要1,2,(这里使用lead函数用于找到下一个时间)
select t2.user_id,
       t2.create_date,
       t2.ran,
       lead(t2.create_date,1,t2.create_date) over (partition by t2.user_id order by t2.create_date) lea
from (
         select t1.user_id,
                t1.create_date,
                rank( ) over (partition by t1.user_id order by t1.create_date) ran
         from (
                  select user_id, create_date
                  from order_info
                  group by user_id, create_date
              ) t1
     ) t2 where t2.ran in (1,2) ; ---过滤第一次下单和第二次下单的时间

--第四步: 根据上述lea字段(为首次下单的第二次下单时间),两者相减=1,则表示首次下单后第二天也下单了
--这里可以使用文档上的过滤1,2后使用最大值,最小值,代替使用lead函数
select t3.user_id,
       t3.create_date,
       t3.lea,
       t3.ran,
       datediff(t3.lea,t3.create_date) df
from (
         select t2.user_id,
                t2.create_date,
                t2.ran,
                lead( t2.create_date , 1 , t2.create_date ) over (partition by t2.user_id order by t2.create_date) lea
         from (
                  select t1.user_id,
                         t1.create_date,
                         rank( ) over (partition by t1.user_id order by t1.create_date) ran
                  from (
                           select user_id, create_date
                           from order_info
                           group by user_id, create_date
                       ) t1
              ) t2  ---where t3.ran in (1,2) 有两种方式,可以先过滤
     )t3  where t3.ran=1;

--第五步: 统计总人数和df=1的个数,求其比值=第一天下单第二天也下单的用户占总用户的比例. round( , 1)保留一位小数
select count(`if`(df=1,1,null)) count_1,    --第二天也下单的用户人数
       count(distinct t4.user_id) count_num,  ---总下单人数
       round(count(`if`(df=1,1,null))/count(distinct t4.user_id)*100,1) --百分比
       ---可以使用concat( ,% )用于拼接%号
from (
         select t3.user_id,
                datediff( t3.lea , t3.create_date ) df
         from (
                  select t2.user_id,
                         t2.create_date,
                         lead( t2.create_date , 1 , t2.create_date )
                               over (partition by t2.user_id order by t2.create_date) lea
                  from (
                           select t1.user_id,
                                  t1.create_date,
                                  rank( ) over (partition by t1.user_id order by t1.create_date) ran
                           from (
                                    select user_id, create_date
                                    from order_info
                                    group by user_id, create_date
                                ) t1
                       ) t2
                  where t2.ran in (1,2)
              ) t3
     )t4;

---方法二: 使用最大值最小值的方式判断.
select concat(round(sum(if(datediff(buy_date_second, buy_date_first) = 1, 1, 0)) / count(*) * 100, 1), '%') percentage
from (
         select user_id,
                min(create_date) buy_date_first,
                max(create_date) buy_date_second
         from (
                  select user_id,
                         create_date,
                         rank() over (partition by user_id order by create_date) rk
                  from (
                           select user_id,
                                  create_date
                           from order_info
                           group by user_id, create_date
                       ) t1
              ) t2
         where rk <= 2
         group by user_id
     ) t3;

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

6. 每个商品销售首年的年份、销售数量和销售金额

  1. 需求: 这个销售数量要找满足首年全年的销售数量,
  2. 分析表: 商品,时间,销售数量=(相同年份相同商品的总销售量), 销售金额=(销售数量*商品单价)
    所以这里考虑(order_detail)
  3. 题意分析: 要找到每个商品最开始销售的那个时间就是首年,然后看商品有多少是在这一年的,格式化销售时间year(),然后可以使用mix()或进行按照商品分区年排序进行排名=1,然后将这个数据按照商品和year进行分组,计算相应的需求.

代码演示

--第一步:使用rank()进行份排序,---这里要的是首年一整年的那个商品,不能使用row_number ,同一年可能在同的时间卖出.
select sku_id,
       year( create_date ) yd,
       rank() over (partition by sku_id order by year(create_date)) year_row,
       price,
       sku_num
from order_detail;
--第二步:过滤出年份排名=1的
select t1.sku_id,
       t1.yd,
       t1.sku_num,
       t1.price
from (
         select sku_id,
                year( create_date ) yd,
                rank( ) over (partition by sku_id order by year( create_date )) year_row,
                price,
                sku_num
         from order_detail
     )t1 where t1.year_row=1;

--第三步:对id,年份进行分组, 同一年可能在同的时间卖出.
select t2.sku_id,
       t2.yd,
       sum(t2.sku_num),
       sum(t2.price*t2.sku_num)
from (
         select t1.sku_id,
                t1.yd,
                t1.sku_num,
                t1.price
         from (
                  select sku_id,
                         year( create_date )                                             yd,
                         rank( ) over (partition by sku_id order by year( create_date )) year_row,
                         price,
                         sku_num
                  from order_detail
              ) t1
         where t1.year_row = 1
     )t2
group by t2.sku_id,t2.yd
order by t2.sku_id;

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

总结

  1. 对于一个需求要考虑全面.
  2. 窗口函数的使用.
  3. 例如题2,使用了一个等差数列的方式,可以更加容易的判断是否是连续的时间.
  4. 例如题4,打标签,拓展需求计算每个等级的用户数量等.
  5. 这里就是为了练习HQL,有什么错误欢迎指正.
  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值