ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数仓工具—Hive语法之行列转换(18)

2021-07-12 18:01:37  阅读:238  来源: 互联网

标签:数仓 group union 18 Hive score time id select


行列转换

今天我们看一个比较常见的计算场景,行列互换,又时候有称之为行列互转

行专列 把多行数据转换成多列数据

列转行 把多列数据转成多行数据

说起来还是有点抽象的,我们还是用一个例子说明一下这两个概念到底是怎么回事,解释清楚之后我们再举几个例子

这是一个学生的成绩表,这里我们就只有一个学生的信息

学生ID科目(subject)分数(score)
1math140
1chinese145
1english143

行专列

下面就是我们行转列侯的结果

学生IDmathchineseenglish
1140145143

列转行

下面就死我们能把上面 行专列 后的数据进行列转行 的结果

学生IDsubjectscore
1math140
1chinese145
1english143

行转列

常规的行转列

这里我们使用 with 进行数据准备工作,select 是我们的核心部分

with data as(
select 1 as id,'math' as subject,140 as score
union all
select 1 as id,'chinese' as subject,145 as score
union all
select 1 as id,'english' as subject,143 as score
)
select
  id,
  max(if(subject='math',score,0)) as math,
  max(if(subject='chinese',score,0)) as chinese,
  max(if(subject='english',score,0)) as english
from
  data
group by
  id
;

汇总数据的行转列

上面的例子其实现实中并不常见,我们看一下现实中的需求是怎么样的

image-20210712160747674

这是一个真实的需求,我们简化一下,我们就看07.05~07.11 的打卡人数,一般情况下我们统计人数,直接按照时间进行group by 就好了

打卡时间打卡人数
20210705100
20210706200

我们往往计算出来的结果是像上面这样的,所以我们需要进行行转列之后才能满足我们的需求。

with data as(
  select 20210706 as time,1 as id
  union all
  select 20210706 as time,2 as id
  union all
  select 20210706 as time,3 as id
  union all
  select 20210707 as time,1 as id
  union all
  select 20210707 as time,2 as id
  union all
  select 20210707 as time,3 as id
)
select
  time,
  count(distinct id) as peo_cnt
from
  data
group by
  time
;

这里我们先汇总计算,计算出每天的人数

time    peo_cnt
+-----+--------+
20210706   3
20210707   3

然后我们再对上面计算出来的数据进行行转列

select
  max(if(time=20210706,peo_cnt,0)) as 0706cnt,
  max(if(time=20210707,peo_cnt,0)) as 0707cnt
from(
  select
    time,
    count(distinct id) as peo_cnt
  from
    data
  group by
    time
)group by
  1

这样我们就计算出来了,需要注意的是我们这里的group by 1

0706cnt	0707cnt
+--------+--------+
3	3
第一次优化

上面我们使用了一个count(distinct id) 其实这个语法是非常不高效的,因为只会适应一个mapper,所以我们就这个写法进行优化一下

select
  max(if(time=20210706,peo_cnt,0)) as 0706cnt,
  max(if(time=20210707,peo_cnt,0)) as 0707cnt
from(
  select
    time,
    count( id) as peo_cnt
  from
  (
    -- 使用group by 进行去重
    select
      time,id
    from
      data
    group by 
      time,id
    ) 
  group by
    time
)group by
  1
第二次优化

我们发现就一个行转列,还这么麻烦,我们能不能在计算的时候就完成行转列的过程,而不是后面再通过group by 来完成这个过程

select
count(if(time=20210706,id,null)) as 0706cnt,
count(if(time=20210707,id,null)) as 0707cnt
from
(
  -- 使用group by 进行去重
  select
    time,id
  from
    data
  group by
    time,id
)
;

这里我们使用if 配合count 来完成行转列的过程,当然你可以使用count_if 函数来替代count if的组合

select
count_if(time=20210706) as 0706cnt,
count_if(time=20210707) as 0707cnt
from
(
  -- 使用group by 进行去重
  select
    time,id
  from
    data
  group by
    time,id
)
;

GROUP BY + COLLECT_LIST/COLLECT_SET

其实这个组合容易被忽略,但是这也是行转列的一种场景,这里我们就不多介绍了,因为我们前面有文站单独写了COLLECT_LIST和COLLECT_SET 的用法和使用场景,请参考Hive语法之collect_set 和 collect_list

列转行

union all 实现

因为列转行很简单,我们就直接使用union all 就可以了,所以我们这里只给一个例子

id	math	chinese	english
+---+-----+--------+--------+
1	140	145	143

这就是我们的数据,我们现在将它展开为多行

select
  id  学生ID,'math' as subject,math as score
from
  data2
union all
select
  id 学生ID,'chinese' as subject, chinese as score
from
  data2
union all
select
  id 学生ID, 'english' as subject,english as score
from
  data2
;

计算结果

学生id	subject	score
+-----+--------+------+
1	math	140
1	chinese	145
1	english	143

注意,这里请使用union all 而不是union,因为这里不需要去重

lateral view 实现

上面的实现我们发现了一个问题,那就是存在大量的union all,如果我们的合并字段很多的话,就需要维护大量的SQL ,所以这里我们看一下lateral view 的实现

,关于lateral view 我们也有单独的文站介绍,请参考Hive语法之explode 和 lateral view

select
  id,subject,score
from
  data2
LATERAL VIEW  explode(
    str_to_map(
      concat(
        'math=',cast(math as string),
        '#chinese=',cast(chinese as string),
        '#english=',cast(english as string)
      )
      ,'#','='
    )
) tmpTable as subject,score
;

这里我们使用了一个函数叫做 str_to_map,其实就是将输入的字符串转化成一个map,然后传递给explode 函数,然后就会生成一个这样的一个临时表

k1,v1
k2,v2
k3,v3

然后这个临时表配合LATERAL VIEW 完成关联操作。

总结

  1. 行转列除了我们使用的汇总函数+if 之外还可以使用统计函数+case when
  2. 其实很多人对行转列和列转行搞不清楚,多行变成一行的就是行转列,因为多行变成了多列,一行变成多行的就是列转行,因为多列变成了多行
  3. 列转行的时候请使用union all 而不是union,因为这里不需要去重

标签:数仓,group,union,18,Hive,score,time,id,select
来源: https://blog.csdn.net/king14bhhb/article/details/118680290

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有