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

本文详细介绍了Hive中的行列转换操作,包括行转列的常规方法、汇总数据行转列的优化技巧,以及列转行的union all和lateral view实现。通过实例解析了行转列的if条件配合count,以及列转行使用union all避免去重的要点。
摘要由CSDN通过智能技术生成

行列转换

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

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

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

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

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

学生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,因为这里不需要去重
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不二人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值