在Hive中会出现许多 数据信息是用Json文件格式来储存的,如开发者对APP上的网页页面开展埋点时,会将好几个字段名储放在一个json二维数组中,因而大数据平台启用数据信息时,要对埋点数据信息开展分析。下面就聊一聊Hive中是怎样分析json数据信息的。
1. get_json_object
select
get_json_object('{"name":"zhangsan","age":18}','$.name');
結果:
name |
---|
zhangsan |
假如既要分析name字段名,也分析age字段名,则能够那样写:
select
get_json_object('{"name":"zhangsan","age":18}','$.name'),
get_json_object('{"name":"zhangsan","age":18}','$.age');
可是假如要分析的字段名有很多,再那样写就太麻烦了,因此 就拥有 json_tuple 这一涵数。
2. json_tuple
select
b.name
,b.age
from tableName a lateral view
json_tuple('{"name":"zhangsan","age":18}','name','age') b as name,age;
name | age |
---|---|
zhangsan | 18 |
留意:上边的json_tuple涵数中沒有$.
假如在应用json_tuple涵数时再加上$.便会分析不成功:
select
b.name
,b.age
from tableName a lateral view
json_tuple('{"name":"zhangsan","age":18}','$.name','$.age') b as name,age;
結果:
name | age |
---|---|
NULL | NULL |
字段名都是NULL,因此 json_tuple涵数不用加$.了,不然会分析不上。
小结:json_tuple等同于get_json_object的优点便是一次能够分析好几个json字段名。可是如果我们有一个json二维数组,这两个涵数都没法解决。
假如有一个hive表,表格中 json_str 字段名的內容以下:
json_str |
---|
[{"website":"baidu.com","name":"百度搜索"},{"website":"google.com","name":"Google"}] |
大家想把这个字段名分析出去,产生以下的构造:
website | name |
---|---|
baidu.com | 百度搜索 |
google.com |
要分析这一json二维数组,仅用上边详细介绍的2个涵数就分析不出来,还要用到以下详细介绍的好多个涵数:
-- 分析array
hive> select explode(array('A','B','C'));
OK
A
B
C
-- 分析map
hive> select explode(map('A',10,'B',20,'C',30));
OK
A 10
B 20
C 30
以上实例将字符串数组中的 oo 或 ar 更换为''。
拥有以上好多个涵数,下面大家来分析json_str字段名的內容:
先将json二维数组中的原素分析出去,转换为每排表明:
hive> SELECT explode(split(regexp_replace(regexp_replace('[{"website":"baidu.com","name":"百度搜索"},{"website":"google.com","name":"Google"}]', '\\]',''),'\\}\\,\\{','\\}\\;\\{'),'\\;'));
OK
{"website":"baidu.com","name":"百度搜索"}
{"website":"google.com","name":"Google"}
对以上sql开展简要说明:
SELECT explode(split(
regexp_replace(
regexp_replace(
'[
{"website":"baidu.com","name":"百度搜索"},
{"website":"google.com","name":"Google"}
]',
'\\[|\\]' , ''), 将json二维数组两侧的中括号除掉
'\\}\\,\\{' , '\\}\\;\\{'), 将json二维数组原素中间的分号换成份号
'\\;') 以分号做为分节符(split涵数以分号做为隔开)
);
为何要将json二维数组原素中间的分号换成份号?
由于原素内的隔开也是分号,如果不将原素中间的分号更换得话,后边用split涵数隔开时也会把原素内的数据信息给隔开,这不是大家要想的結果。
上步早已把一个json二维数组转换为好几个json字符串数组了,下面融合son_tuple涵数来分析json里边的字段名:
select
json_tuple(explode(split(
regexp_replace(regexp_replace('[{"website":"baidu.com","name":"百度搜索"},{"website":"google.com","name":"Google"}]', '\\[|\\]',''),'\\}\\,\\{','\\}\\;\\{'),'\\;'))
) , 'website', 'name') ;
实行以上句子,結果出错了:
FAILED: SemanticException [Error 10081]: UDTF's are not supported outside the SELECT clause, nor nested in expressions
意思是UDTF涵数不可以写在其他涵数内,也就是这儿的explode涵数不可以写在json_tuple里边。
即然explode涵数不可以写在其他json_tuple里边,那大家可以用子查询方法,以下所显示:
select json_tuple(json, 'website', 'name')
from (
select explode(split(regexp_replace(regexp_replace('[{"website":"baidu.com","name":"百度搜索"},{"website":"google.com","name":"Google"}]', '\\[|\\]',''),'\\}\\,\\{','\\}\\;\\{'),'\\;'))
as json) t;
实行以上句子,沒有出错,实行結果以下:
www.baidu.com 百度搜索
google.com Google
hive表格中 goods_id 和 json_str 字段名的內容以下:
goods_id | json_str |
---|---|
1,2,3 | [{"source":"7fresh","monthSales":4900,"userCount":1900,"score":"9.9"},{"source":"jd","monthSales":2090,"userCount":78981,"score":"9.8"},{"source":"jdmart","monthSales":6987,"userCount":1600,"score":"9.0"}] |
目地:把 goods_id 字段名和 json_str 字段名中的monthSales分析出去。
下边大家就逐渐分析:
分拆goods_id字段名及将json二维数组转换成好几个json字符串数组:
select
explode(split(goods_id,',')) as good_id,
explode(split(regexp_replace(regexp_replace(json_str , '\\[|\\]',''),'\\}\\,\\{','\\}\\;\\{'),'\\;'))
as sale_info
from tableName;
实行以上句子,結果出错:
FAILED: SemanticException 3:0 Only a single expression in the SELECT clause is supported with UDTF's. Error encountered near token 'sale_info'
意思是用UDTF的情况下,SELECT 只适用一个字段名。而以上句子select中有两个字段名,因此 出错了。
那怎么办呢,要处理这个问题,还得再详细介绍一个hive英语的语法:
lateral view用以和split、explode等UDTF一起应用的,能将一行数据信息拆分为几行数据信息,在这个基础上能够对分拆的数据信息开展汇聚,lateral view最先为初始表的每排启用UDTF,UDTF会把一行拆分为一行或是几行,lateral view在把結果组成,造成一个适用别称表的虚似表。
假定大家有一张客户个人爱好表 hobbies_table,它有多列数据信息,第一列是name,第二列是客户兴趣爱好爱好的id_list,是一个二维数组,储存兴趣爱好爱好的id值:
name | id_list |
---|---|
zhangsan | [1,2,3] |
lisi | [3,4,5] |
我们要统计分析全部兴趣爱好id在全部客户中发生的频次:
对兴趣爱好id开展分析:
SELECT name, hobby_id
FROM hobbies_table
LATERAL VIEW explode(id_list) tmp_table AS hobby_id;
以上sql实行結果:
name | hobby_id |
---|---|
zhangsan | 1 |
zhangsan | 2 |
zhangsan | 3 |
lisi | 3 |
lisi | 4 |
lisi | 5 |
2. 依照hobby_id开展排序汇聚就可以:
SELECT hobby_id ,count(1) client_num
FROM hobbies_table
LATERAL VIEW explode(id_list) tmp_table AS hobby_id
group by hobby_id;
結果:
hobby_id | client_num |
---|---|
1 | 1 |
2 | 1 |
3 | 2 |
4 | 1 |
5 | 1 |
详细介绍完 lateral view 以后,大家再说处理上边碰到的用UDTF的情况下,SELECT 只适用一个字段名的难题:
select good_id,get_json_object(sale_json,'$.monthSales') as monthSales
from tableName
LATERAL VIEW explode(split(goods_id,','))goods as good_id
LATERAL VIEW explode(split(regexp_replace(regexp_replace(json_str , '\\[|\\]',''),'\\}\\,\\{','\\}\\;\\{'),'\\;')) sales as sale_json;
留意:以上句子是三个表笛卡尔积的結果,因此 此方法适用信息量并不是非常大的状况。
以上句子实行結果以下:
goods_id | monthSales |
---|---|
1 | 4900 |
1 | 2090 |
1 | 6987 |
2 | 4900 |
2 | 2090 |
2 | 6987 |
3 | 4900 |
3 | 2090 |
3 | 6987 |
假如表格中也有别的字段名,我们可以依据别的字段名挑选出合乎結果的数据信息。
小结:lateral view一般和UDTF一起发生,为了更好地处理UDTF不允许在select存有好几个字段名的难题。