前言
hive中有很大部分的函数是基于复杂类型去计算的,因为我们现实中的数据往往不是f(x)->y这种类型的,更多其实是f(x1,x2,x3)->y的类型。在这种场景下,hive提供了复杂类型的操作。数据的结构类型会决定udf作用的目标也不一样,这种场景下的udf往往会通过多步组合来得到我们想要的结果。
hive中的复杂类型盘点
类型 | 说明 |
ARRAY | 包含同类型元素的数组,索引从0开始 ARRAY |
MAP | 字典 MAP<primitive_type, data_type> |
STRUCT | 结构体 STRUCT<col_name : data_type [COMMENT col_comment], …> |
UNIONTYP | 联合体 UNIONTYPE<data_type, data_type, …> |
这些数据类型同样提供了udf来生成,我们可以在源码中找到这部分:
system.registerGenericUDF("array", GenericUDFArray.class); system.registerGenericUDF("assert_true", GenericUDFAssertTrue.class); system.registerGenericUDF("map", GenericUDFMap.class); system.registerGenericUDF("struct", GenericUDFStruct.class); system.registerGenericUDF("named_struct", GenericUDFNamedStruct.class); system.registerGenericUDF("create_union", GenericUDFUnion.class); system.registerGenericUDF("extract_union", GenericUDFExtractUnion.class);
常用UDF盘点
有了之前的准备,我们可以开始我们的udf解释了:
集合处理类函数
array
这个函数就是生成我们一个数组类型的结果:
select array('facebook', 'com','a') as url; ["facebook","com","a"]
需要注意显示的时候其实是会带有[]的字样
sort_array
这个函数其实是对我们的数组类似的结果进行排序,所以这个函数需要传入数组
select sort_array(array('facebook', 'com','a')) as url; ["a","com","facebook"]
用数字会更加明显一些:
select sort_array(array(1,3,6,5,8,9)) as url; [1,3,5,6,8,9]
map
map对应的数据格式就是(k,v)的结构,我们用这个函数去构造map类型的数据对象:
select map("1","宋江","2","林冲") {"1":"宋江","2":"林冲"}
聚合类函数
聚合函数需要作用在一个group的数据组中,对group by之后的结果进行处理,为了说明问题,我需要准备点数据,
表的结构情况:
desc temp.test_tab; num int a string g1 string
表数据如下:
select * from temp.test_tab; 1 A g 2 B g 3 C g 4 C g 4 NULL g
group_concat
函数说明:group_concat(col1, isUnique) - Aggregation function, collect all non-null col1 value into an array, if isUnique is non-zero, remove the duplicated values.
采集所有非空的列上面的值到一个数组中,isUnique如果非0,则会去掉重复的值。
我们先参数传0
select g1,group_concat(a,0) from temp.test_tab group by g1 g ["A","B","C","C"]
再来一波非0的
select g1,group_concat(a,1) from temp.test_tab group by g1; g ["A","B","C"]
collect_list
函数说明:Returns a list of objects with duplicates
这个函数其实就是返回数据聚合之后的数组对象,这里特别强调了一下会带重复
select g1,collect_list(a) from temp.test_tab group by g1 g ["A","B","C","C"]
collect_set
函数说明:Returns a set of objects with duplicate elements eliminated
这个函数和上面的函数想呼应,只不过结果会去重
select g1,collect_set(a) from temp.test_tab group by g1; g ["A","B","C"]
需要说明的是,这样子返回去重的数据是不保证顺序的,实际情况我们需要顺序的时候会配合sort_array函数一起使用:
select g1,sort_array(collect_set(num)) from temp.test_tab group by g1 g [1,2,3,4]
concat_ws
这个函数在之前也有聊过,事实上,这个函数传入的类型可以是数组和简单类型的组合,放在这里说的原因是这个函数往往和上面的聚合函数进行组合
select concat_ws('.', 'www', array('facebook', 'com')) as url; www.facebook.com
因为可以传入数组,所以我们实际使用的时候会用把这个函数套上去,得到我们要的结果:
select g1,concat_ws(',',collect_list(a)) from temp.test_tab group by g1 g A,B,C,C
这次的变化在于,返回的结果已经是一个字符串类型了
转置类操作
explode
这个函数就是把横着的扳直,作用在map结构和list结构上
select explode(array(1,3,6,5,8,9)) as id id 1 3 6 5 8 9
map类型输出的列名就是key和value
select explode(map("1","宋江","2","林冲")) key value 1 宋江 2 林冲
lateral view
这个其实是一个操作,因为基于explode操作之后,我们的表数据会被硬生生一行变成多行了,这个和原有的数据需要有一个对应的关系
比如我们之前的sql:
select g1,sort_array(collect_set(num)) as id from temp.test_tab group by g1
这个生成的结果就是:
g [1,2,3,4]
但是我们如果进行explode:
select g1,explode(sort_array(collect_set(num))) as id from temp.test_tab group by g1 • 1
这个操作其实就是类似两张表去做join操作了,类似这样:
g [1,2,3,4] => 要变成 g 1 g 2 g 3 g 4
这种情况的话在sql运算中其实没有做子查询的时候是不能实现的,这个时候就做了一个破坏了数据操作范式的语法lateral,把explode运算的结果,和原有的数据做一次类似join的操作,我们可以看下源码上对这一操作的描述:
The lateral view join operator is used for FROM src LATERAL VIEW udtf()... * This operator was implemented with the following operator DAG in mind. * * [Table Scan] * | * [Lateral View Forward] * / \ * [Select](*) [Select](adid_list) * | | * | [UDTF] (explode) * \ / * [Lateral View Join] * | * | * [Select] (pageid, adid.*) * | * ....
later view 在实际操作中是做了一次later view join的操作,结果集就会达到join的效果,举例说明:
select num,id from temp.test_tab lateral view explode(array(1,3))q as id; • 1
注意id的列名的来自后面生成的as id列,我们看图中的结果数据,每一个num值都和新的1 3关联上了,这个其实数据量也翻了两倍了
这个函数除了在把数据拉直的时候处理之外,还有个场景是把join两边数据不均匀的时候提前做分组,这样子可以避免数据倾斜。