添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
首发于 Hive SQL篇

在互联网公司实习中使用Hive SQL的一些体会和注意点

在互联网公司实习已经一个多月了,在这一个多月的时间里,大部分都是在接业务方需求,取数,做报表的过程中,从第二个月初就开始慢慢接分析需求了。

现在主要想给大家分享下,我在公司实习时使用hive sql获取数据过程中的一些注意点:

1、将字符串日期转为整型日期

hive数据库中存储的时间是string类型的,但是我们在取数的时候,习惯把字符串型的日期转换为整型的日期,如将'2020-09-03'转换成'20200903',转换方式如下:

1regexp_replace(substr(create_time,1,10),'-','')
2regexp_replace(to_date(create_time),'-','')

2、将整型日期转为字符串日期

公司使用的hive数据库中,无论是增量表还是全量表,都是以天作为分区字段的,且分区字段采用的是整型格式,如果我们需要求新增用户、次留用户等,会涉及到datediff()、date_sub()函数的计算,这时候需要将整型的日期转换成字符串类型的日期格式。

(1)将INT类型的日期值使用cast()函数转成STRING类型,用Hive内置的unix_timestamp函数转成时间戳类型,最后将时间戳用from_unixtime转成yyyy-MM-dd的日期类型。

from_unixtime(unix_timestamp(cast(day as string),'yyyymmdd'),'yyyy-mm-dd')

(2)将INT类型的日期值转成STRING类型,再对字符串进行截取处理,用-拼接起来

concat(substr(cast(day as string),1,4),'-',substr(cast(day as string),5,2),'-',substr(cast(day as string),7,2))

3、在json字符串中提取出想要的字段和值,可以使用get_json_object()

cast(get_json_object(label,'$.trace_id') as bigint)

4、将查询结果中的空值给个默认值,使用nvl()函数

--如
nvl(area,'OTHER') 

这个语句的含义是:如果大区表中存在为空的记录,则可以使用OTHER来代替

5、如果查询结果是按照男女分组,但需要男女合计的结果,则可以使用在分组条件中使用with cube函数,则本来应该只有两条记录的,使用with cube;

select gender,
          count(distinct user_id) 
from XXX 
where day=20200902 --分区字段
group by  gender with cube;

则结果应该为:男 XX

女 XX

XX --此字段未合计字段,即是男和女的人数相加的结果

6、在连接表中使用on 1=1的条件

(1)使用on 1=1的条件是在两个表中计算出的指标的情况,是没有聚合函数的情况下,才可以正常使用;

(2)如表1是select sum(字段) from 表1,表2是select count(字段) from 表2 on 1=1,这种情况会出现数据膨胀的情况

7、在连接表时,需要注意表的连接条件的字段类型是否是一致的,如果出现不一致的情况,需要进行转换。

如:表1和表2的连接条件为user_id,如果表1的user_id是bigint型,表2的user_id是string型,如果直接连接,则会导致数据出现的膨胀的情况,则需要对数据进行转换,如cast(user_id as bigint),,将string类型的user_id转换成整型的user_id。

select count(distinct t1.user_id) as urs
select user_id
from A
) as t1
left join
select cast(user_id as bigint) as user_id
from B
) as t2
on t1.user_id=t2.user_id

8、工作中常用的时间和日期函数

(1)返回查询时刻的当前日期

current_date  

(2)返回查询时刻的当前时间

current_timestamp()  

(3)返回开始日期startdate与结束日期enddate之前相差的天数

datediff(string enddate, string startdate) 

(4)在startdate基础上加上几天,返回加上几天之后的一个日期

date_add(DATE startdate, INT days)  

(5)在startdate基础上减去几天,返回减去几天之后的一个日期,功能与date_add很类似

date_sub(DATE startdate, INT days) 

(6)将date/timestamp/string类型的值转换为一个具体格式化的字符串。

date_format(DATE|TIMESTAMP|STRING ts, STRING fmt)
select date_format('2020-09-03', 'yy/MM/dd')
select date_format('2020-09-03', 'yy-MM-dd')
select date_format('2020-09-03', 'yyMMdd')
select date_format('2020-05-20', 'yyyy年MM月dd日 HH时mm分ss秒')

9、如何避免出现数据倾斜的情况?

我现在主要接触的是hive数据库,在取数的过程中要注意避免出现数据倾斜的情况,在我的工作中,可能会出现数据倾斜的情况主要有:在分类字段中,其中A字段占60%,但只有一个取值,B字段占39%,也只有一个取值,而C字段占1%,但取值非常多。

如:性别字段,其中男占60%,而女占39%,而其他很多取值占1%,如果直接做连接的话,可能会出现数据倾斜的情况。

针对这种情况,我们需要先在最内层,将其他多种取值全部放到other字段中,如下:

select  if(gender in ('男','女'),gender,'OTHER') as gender
from XXX
# 将其他的取值全部归为other,然后再去做连接

10、在计算用户留存的时候,尽量避免在where条件中使用datediff()日期函数进行计算

我们平常时在看SQL的面试题和其他的题目的时候,在计算用户留存或其他的情况,会习惯在where条件中使用用datediff(),两个日期相减,得到用户第二天还使用APP的用户,但以我的实习经历来看,我的leader也会引导我,避免在where中使用datediff()日期函数,正确的用法如下:

select t1.day
          ,count(distinct t1.user_id) as new_urs
          ,count(distinct t2.user_id) as new_retention_urs
(select day
          ,user_id
          ,date_add(from_unixtime(unix_timestamp(cast(day as string),'yyyymmdd'),'yyyy-mm-dd'),1) as next_date
from A表
where XX
) as t1
left join
(select day
          ,user_id
         ,from_unixtime(unix_timestamp(cast(day as string),'yyyymmdd'),'yyyy-mm-dd') as date
from B表
where XX
) as t2
on t1.user_id=t2.user_id and t1.next_date=t2.date
# A表当天日期加上1,就是第二天,所以用A表的next_date等于B表的date,就可以计算出用户的留存了

11、如何在分区表中删除数据?

有时候,我们在tableau上做报表后,需要重新回滚数据,或不需要前面日期的数据,我们就可以在hive中将之前的数据删除,这里主要是将分区日期删除,那么对应分区的数据就会被删除。

alter table A库.A表 drop partition (day<=20201025)
# 20201025日以及之前的数据删除掉。

12、如何将行转列

最近接触了新的需求,有些表格将一些字段存储在一个列表中,我需要将其转换为行,然后进行关联,就涉及到了行转列的使用。

如将useridList->[123,345,234,2334]转为
2334

这就需要使用到行转列,注意,这里需要将括号去掉。

select userid2
select substr(useridList2length(useridList)-2) as userid
from a
) t1
lateral view explode(split(userid,',')) t1 as userid2

13、hive中修改表字段类型/添加新字段

alter table 表名 change column 原字段名称 现字段名称 数据类型
# 添加新字段
alter table 表名 add columns(字段名称 字段类型 comment '字段的解释')

14、查询分区/求中位数

show partitions 表名
# 求中位数
percentile_approx(字段名,0.5)

15、解决with cube分组过多的情况

set hive.new.job.grouping.set.cardinality=32; 
#其中32是根据你分组的2^(分组个数)来决定的

16、解决日志表中存在带“$”符号的字段取值问题(2021/10/13更新)

由于采用的埋点方式不一样,在工作中埋点底层表会存在一些带$符号的字段,但由于数据库存的数据类型是string类型,而不是map类型,对于带$符号的字段值提取就要采用其他的方式,如下:

select day
       ,count(distinct userid) as urs