本文来介绍,在Hive中如何实现列转行的操作。
假设Hive表中有两列数据,数据形式如下:
a 1
a 2
a 3
a 3
b 4
b 5
b 6
b 6
现希望将展现形式改为下面这种,依旧是两列数据,但第二列数据 将a、b所对应的一列所有数字都分别放到了一行数据中,并以逗号等指定分隔符分隔。
a 1,2,3,3
b 4,5,6,6
或者将展现形式改为下面这种,将原本第二列里的每个数据单独转为一列。
a 1 2 3 3
b 4 5 6 6
这两种操作就叫做列转行。
在Hive中,并没有一个函数可以直接实现列转行的操作,需要使用两个或三个函数进行嵌套才能达成,涉及下表的几个函数,split在进阶篇(二)中已有介绍,这里着重介绍下另外三个函数。
返回数值类型 | 函数名\所需参数\参数数据类型 | 描述 |
---|---|---|
string |
concat_ws(string SEP, array |
拼接Array中的元素并用指定分隔符进行分隔 |
array |
collect_set(col) |
返回消除了重复元素的数组 |
array |
collect_list(col) |
返回允许重复元素的数组 |
功能:字符串连接函数,之前在进阶篇(二)中介绍过一种它的常用语法,这里介绍一种新的语法,在第二个参数位置传入一个元素数据格式为string的数组,将数组中的元素使用第一个参数传入的指定分隔符进行连接。
举例:
hive (app)> select concat_ws('-',array('a','b'));
a-b
功能:将该列中的所有元素去重后以数组的形式返回
举例:
新建test.txt文件,输入上文的两列数据,以逗号分隔
[root@hadoop ~]# vim test.txt
a,1
a,2
a,3
a,3
b,4
b,5
b,6
b,6
在hive中新建表temp_test5,将test文件中的数据插入,查看数据
CREATE TABLE temp_test5 (
shop STRING
,shangpin_num INT
) row format delimited fields terminated BY ',';
load data local inpath '/root/test.txt' into table temp_test5;
select * from temp_test5;
temp_test5.shop temp_test5.shangpin_num
a 1
a 2
a 3
a 3
b 4
b 5
b 6
b 6
使用collect_set,可以看到collect_set为聚合函数,配合group by,将shangpin_num中的元素去重后以数组的形式返回:
SELECT shop
,collect_set(shangpin_num)
FROM temp_test5
GROUP BY shop;
shop _c1
a [1,2,3]
b [4,5,6]
功能:将该列中的所有元素以数组的形式返回,与collect_set的唯一区别是不会进行去重
举例:
SELECT shop
,collect_list(shangpin_num)
FROM temp_test5
GROUP BY shop;
shop _c1
a [1,2,3,3]
b [4,5,6,6]
collect_set和collect_list返回的是一个数组,而concat_ws的第二个输入参数也正好要求是数组。看到这里,大家可能已经很清楚如何组合函数进行列转行操作了,不过还是给大家演示一下~
注意:如果collect_set和collect_list中的列数据格式必须为string格式,否则报错如下:
hive (app)> SELECT shop
> ,concat_ws('-',collect_list(shangpin_num))
> FROM temp_test5
> GROUP BY shop;
FAILED: SemanticException [Error 10016]: Line 2:21 Argument type mismatch 'shangpin_num': Argument 2 of function CONCAT_WS must be "string or array", but "array" was found.
这里使用cast函数进行格式转换,concat_ws的第一个参数是分隔符,大家看自己需求使用:
SELECT shop
,concat_ws('-',collect_set(cast(shangpin_num as string)))
FROM temp_test5
GROUP BY shop;
shop _c1
a 1-2-3
b 4-5-6
如果想将shangpin_num中去重后的元素各自作为一列,直接使用数组的下标将其分别取出即可:
SELECT shop
,collect_set(shangpin_num)[0]
,collect_set(shangpin_num)[1]
,collect_set(shangpin_num)[2]
FROM temp_test5
GROUP BY shop;
shop _c1 _c2 _c3
a 1 2 3
b 4 5 6
想要实现不去重列转行,将collect_set换为collect_list即可:
SELECT shop
,concat_ws('-',collect_list(cast(shangpin_num as string)))
FROM temp_test5
GROUP BY shop;
shop _c1
a 1-2-3-3
b 4-5-6-6
如果想将shangpin_num中所有的元素各自作为一列,同理,直接使用数组的下标将其分别取出即可:
SELECT shop
,collect_list(shangpin_num)[0]
,collect_list(shangpin_num)[1]
,collect_list(shangpin_num)[2]
,collect_list(shangpin_num)[3]
FROM temp_test5
GROUP BY shop;
shop _c1 _c2 _c3 _c4
a 1 2 3 3
b 4 5 6 6
能看到这里的同学,就右上角点个赞吧,3Q~