hive substr函数_数据分析工具篇——HQL函数及逻辑

本篇文章我们梳理一下hive常用的函数,对于hive而言,常用的函数并不是特别多,往往记住关键几个,就可以解决80%的问题,这也是大家喜欢hive的原因,那么,常用的函数有哪些呢?

时间函数

b9d6566a756d736617f35cb54f0381ab.gif3a33695b07b45a70a352e39acd1778e7.png

1)时间格式转化

常用的日期格式主要有两个:时间戳和日期格式,时间戳便于计算,日期格式便于阅读,两者各有利弊,所以需要相互转换,转换的对应函数主要有:

from_unixtime(timestamp,dateformat):将时间戳转化为日期格式,格式必须是10位,毫秒级的时间戳需要用cast转化成秒级。unix_timestamp(date,dateformat):日期格式转化为时间戳,如果括号内没有参数则表示返回当前的时间戳。

例如:

时间戳转日期格式:

select  from_unixtime(unix_timestamp(),'yyyy-MM-dd HH:mm:ss') as date_time,  from_unixtime(1537924406,'yyyy-MM-dd') as date_time1from dual;

如果时间戳是毫秒级,则字段修改为:

from_unixtime(cast(unix_timestamp()/1000 as int),'yyyy-MM-dd HH:mm:ss') as date_time

日期格式转时间戳:

select  unix_timestamp() as time_stamp,  --返回当前时间戳;  unix_timestamp('2018-09-26 9:13:26','yyyy-MM-ddHH:mm:ss') as time_stamp1from dual;

作为一个数据,在不同系统之间游走,有没有经常遇到时间格式不匹配的情况,所以需要做格式的转化:

date_format(string/date,dateformate):把字符串或者日期转成指定格式的日期。

日期格式调整:

select  date_format('2018-09-12','yyyy-MM-dd HH:mm:ss') as date_time,  date_format('2018-09-12','yyyyMMdd') as date_time1from dual;--2018-09-12 00:00:00

有没有发现一个问题,date_format只能从yyyy-MM-dd格式转化成其他格式,对于其他类型,例如:yyyyMMdd,则不能处理,所以需要灵活一点,将各个格式的时间转化成时间戳,再通过时间戳转化成其他格式:

select from_unixtime(unix_timestamp(substr('20191007000000',1,8),'yyyyMMdd'),'yyyy-MM-dd');

如此一来,各种格式都可以转化。

2)日期的加减差运算:

日期的加减是对日期格式数据的基本运算,常用的是时间差计算、加一天、减一天等。

时间差计算:

datediff(date,date1);日期差,即两个日期差几天。months_between(date,date1);两个日期差几个月,用法一致。select datediff('2015-05-22','2015-05-29') as date_ff; --[-7]

在原日期上加n天:

date_add:在现在日期上增加天数,

add_months是增加月份,用法一致。

select date_add('2015-05-22 15:34:23',2); --2015-05-24

在原日期上减n天:

date_sub:在现在日期上减少天数。

select date_sub('2015-05-22 15:34:23',2); --2015-05-20

3)时间截取:

如果我们获取到一个完整的时间,但是只想用其中的一部分,势必牵扯到时间截取的功能,常见的时间截取主要是如下几个函数:

to_date:获取完整时间中的日期部分:

select to_date('2015-06-01 15:34:23'); --2015-06-01

year:获取完整时间中的年份:

select year('2015-05-22 15:34:23'); --2015

month:获取完整时间中的月份:

select month('2015-05-22 15:34:23'); --5

day:获取完整时间中的日期:

select day('2015-05-22 15:34:23'); --22

4)日期中其他灵活操作:

   日期除了计算多少天,截取某一段,还会牵扯一些比较灵活的操作方法,例如:判断当天是周几,判断年周、月周,当月的最后一天和第一天,下n天,上n天等。

   获取一年中的第几周:weekofyear:

select weekofyear('2015-05-22 15:34:23'); --21

获取当月中的第几周:

select weekofyear('2015-05-22')-weekofyear(trunc('2015-05-22', ‘MM’))+1;

这其中有一个有意思的点:每一周是按照周一到周日为一个完整周,与英美的周日到周六的逻辑有些不一样,所以在如下日历中:

a2d2ee2a7d8533212bb4b2bb3a045be1.png

11月1日为周日,按照代码中的逻辑与10月31日是一周。

判断当天为周几:

Select 8-datediff(next_day(‘2020-12-20’, ‘MO’), ‘2020-12-20’) as week;

获取月末最后一天: 

last_day(date):

select  last_day('2018-09-30') as date_time,  last_day('2018-09-27 21:16:13') as date_time1from dual;--2018-09-30

获取月初、年初:

trunc(date,format) 

format:MONTH/MON/MM, YEAR/YYYY/YY

select  trunc('2018-09-27','YY') as date_time,--返回年初  trunc('2018-09-27 21:16:13','MM') as date_time1  --返回月初from dual;--2018-01-01和2018-09-01

当前日期下个星期X的日期:

next_day(date,formate) format:英文星期几的缩写或者全拼。

select  next_day('2018-09-27','TH') as date_time,  next_day('2018-09-27 21:16:13','TU') as date_time1from dual;--2018-10-04和2018-10-02

汇总SQL为(下文SQL为对应的日期写法,可以作为上面简单罗列的补充版):

select     day                                                                                                   -- 时间    ,date_add(day,1 - dayofweek(day))                                                  as week_first_day   -- 本周第一天_周日    ,date_add(day,7 - dayofweek(day))                                                  as week_last_day    -- 本周最后一天_周六    ,date_add(day,1 - case when dayofweek(day) = 1 then 7 else dayofweek(day) - 1 end) as week_first_day   -- 本周第一天_周一    ,date_add(day,7 - case when dayofweek(day) = 1 then 7 else dayofweek(day) - 1 end) as week_last_day    -- 本周最后一天_周日    ,next_day(day,'TU')                                                                as next_tuesday     -- 当前日期的下个周二    ,trunc(day,'MM')                                                                   as month_first_day  -- 当月第一天    ,last_day(day)                                                                     as month_last_day   -- 当月最后一天    ,to_date(concat(year(day),'-',lpad(ceil(month(day)/3) * 3 -2,2,0),'-01'))          as season_first_day -- 当季第一天    ,last_day(to_date(concat(year(day),'-',lpad(ceil(month(day)/3) * 3,2,0),'-01')))   as season_last_day  -- 当季最后一天    ,trunc(day,'YY')                                                                   as year_first_day   -- 当年第一天    ,last_day(add_months(trunc(day,'YY'),12))                                          as year_last_day    -- 当年最后一天    ,weekofyear(day)                                                                   as weekofyear       -- 当年第几周    ,second(day)                                                                       as second           -- 秒钟    ,minute(day)                                                                       as minute           -- 分钟    ,hour(day)                                                                         as hour             -- 小时    ,day(day)                                                                          as day              -- 日期    ,month(day)                                                                        as month            -- 月份    ,lpad(ceil(month(day)/3),2,0)                                                      as season           -- 季度    ,year(day)                                                                         as year             -- 年份from (    select '2018-01-02 01:01:01' as day union all    select '2018-02-02 02:03:04' as day union all    select '2018-03-02 03:05:07' as day union all    select '2018-04-02 04:07:10' as day union all    select '2018-05-02 05:09:13' as day union all    select '2018-06-02 06:11:16' as day union all    select '2018-07-02 07:13:19' as day union all    select '2018-08-02 08:15:22' as day union all    select '2018-09-02 09:17:25' as day union all    select '2018-10-02 10:19:28' as day union all    select '2018-11-02 11:21:31' as day union all    select '2018-12-02 12:23:34' as day) t1;

数据运算总结

b9d6566a756d736617f35cb54f0381ab.gif3a33695b07b45a70a352e39acd1778e7.png

Hive中数据一般是可以用常规的运算符号表示的,例如:“加减乘除”用“+-*/”表示,但是对于初次之外的运算需要用到对应的函数:

sum():求和;count():求数据量;avg():求平均值;distinct:求不同值数(去重);min:求最小值;max:求最大值;pmod(int a, int b):返回a除以b的余数的绝对值;round:四舍五入 select round(数值,小数点位数);ceil:向上取整 select ceil(45.6); floor:向下取整 select floor(45.6);

在数据运算过程中经常会遇到用字符串形式存储的数值,计算过程中需要将其转化成数值型,然后进行运算,其中运算方法为:

cast(aaa as int):将string转化成int;cast(aaa as decimal(10, 2)):将string转化成float,保留两位小数;

当然也可以将数值转化成字符串:

cast(aaa as string):将int转化成string;

除了上面这些方法外,hive在数据运算方面还有大于、小于、等于等操作,这些操作都有对应的运算符号表示,在此就不多描述了。

字符串操作总结

b9d6566a756d736617f35cb54f0381ab.gif3a33695b07b45a70a352e39acd1778e7.png

字符串可以说是最常用的处理思路,在特征处理、数据清洗过程中高频使用,这些函数用的好会为接下来数据建模、统计运算带来极大便利。下面我们来梳理一下常用的字符串处理方法:

1)空格处理:

trim(String A):去除A两侧的空格;ltrim(String A):去除左边空格;rtrim(String A):去除右边空格select trim('abc') from lxw_dual;

2)单个字符串操作:

reverse('abcde')=edcba:字符串反转

   lower:转成小写

select lower('Hive'); --hive

upper:转成大写

select lower('Hive'); --HIVE

length:长度

select length('Hive'); --4 

substr:求子串

select substr('hive',2); --iveselect substr('hive',2,1); --i

lpad:左填充

对hive填充到10位,补位用#

select lpad('hive',10,'#'); --######hive

rpad:右填充

select rpad('hive',10,'#'); --hive######

regexp_extract():正则表达式处理

regexp_extract(string subject, string pattern, int index); --函数的应用;

将字符串subject按照pattern正则表达式的规则拆分,返回index指定的字符:

select  clientcode,  regexp_extract(filterlist,'(filtertype"\\:")(\\d+)(",)',2) as filtertypefrom tmp_action_click

在正则表达式中,经常会用到贪心算法(.*?)和非贪心算法(.*)用法,用来清洗字符串中的内容。

正则表达式替换函数:

regexp_replace('foobar','oo|ar','')=fb #替换oo|ar为空字符

有意思的是,hive中并没有直接的replace函数,如果是整个字符串的判断可以用case when的方式操作,如果只替换其中一部分可以用regexp_replace操作。

get_json_object(string json_string,string path)

该函数的第一个参数是json对象变量,第二个参数使用$表示json变量标识,然后用.或[]读取对象或数组。

例如:

如果json为:

Json={"store":{"fruit":\[{"weight":8,"type":"apple"},{"weight":9,"type":"pear"}],               "bicycle":{"price":19.95,"color":"red"}                },      "email":"amy@only_for_json_udf_test.net",      "owner":"amy"     }

对应的SQL为:

SELECT get_json_object(json, '$.owner') FROM src_json;--amySELECT get_json_object(json, '$.store.fruit\[0]') FROM src_json;--{"weight":8,"type":"apple"}SELECT get_json_object(json, '$.non_exist_key') FROM src_json;--NULL

第二个常用的函数为:

json_tuple(string json_string,string k1,string k2...)

该函数的第一个参数是json对象变量,之后的参数是不定长参数,是一组键k1,k2...,返回值是元组,该方法比get_json_object高效,因为可以在一次调用中输入多个键值。

select  src.timestamp,  b.*from src_json srclateral view json_tuple(src.json, 'email', 'owner') b as f1, f2;

   另外,单个字符串如果是url,可以直接使用pares_url函数,个人感觉这个函数比较鸡肋,看起来很有用,但是大部分功能可以用string拼接的方式搞定。

URL解析函数:parse_url

语法: 

parse_url(string urlString, string partToExtract [, stringkeyToExtract])

返回值: string

说明:返回URL中指定的部分。

partToExtract的有效值为:HOST, PATH, QUERY, REF, PROTOCOL, AUTHORITY, FILE, and USERINFO.

举例:

select  parse_url('http://facebook.com/path1/p.php?k1=v1&k2=v2#Ref1', 'HOST') from lxw_dual;--facebook.comselect  parse_url('http://facebook.com/path1/p.php?k1=v1&k2=v2#Ref1', 'QUERY','k1')from lxw_dual--v1

应用过程中还有一个k-v值的函数:

str_to_map(String text,String delimiter1,String delimiter2)

使用两个分隔符将文本拆分成键值对。Delimiter1将文本分成k-v对,Delimiter2分割每个k-v对。对于delimiter1的默认值是',',delimiter2的默认值是'='。

select str_to_map('abc:11&bcd:22', '&', ':')--[“abc”:“11”, “bcd”:“22”]

3)多字符串操作:

   concat():字符串拼接

concat('aa','bb','cc')=aabbcc

   concat_ws(sep,str1,str2,...):根据固定的分隔符连接后侧字符串;

   concat_ws第一个参数是其它参数的分隔符,分隔符的位置放在要连接的两个字符串之间,分隔符可以是一个字符串,也可以是其他参数。

Select concat_ws(',','11','22','33'); --11,22,33

split(str,regex):数据切分。

该函数第一个参数是字符串,第二个参数是设定的分隔符,通过第二个参数把第一个参数做拆分,返回一个数组:

select split('123,3455,2568',',')--[“123”,”123”,”123”]

字段的数据处理

b9d6566a756d736617f35cb54f0381ab.gif3a33695b07b45a70a352e39acd1778e7.png

特征处理过程中也会经常遇到条件处理,例如:是否为空、满足什么条件、排序等,我们简单做一下了解:

1)是否为空的判断:

select nvl(T v1, T default_value);-- 如果v1不为null返回v1,否则default Value。select nvl(null,0);select coalesce(T v1, T v2, T v3, ...);--返回第一个不为null的value值。select COALESCE(null,'aaa',50)from default.dual limit 1;--aaaselect COALESCE(1,'aaa',50)from default.dual limit 1;--1

2)条件判断:

select if(boolean testCondition, T valueTrue, T valueFalseOrNull);select if(1=1,'bbbb',111);--if条件判断表达式。

3)row_number()函数:分组排序的功能。

row_number() over(distribute by col1 sort by clo2 desc)

字段的行列变化

b9d6566a756d736617f35cb54f0381ab.gif3a33695b07b45a70a352e39acd1778e7.png

Hive中比较有特色的功能是行列变化,可以实现多行变一行,也可以实现一行变多行,但是其中有个缺陷,就是多行变多列无法实现,会用python的同学可以通过pyspark实现对应的变化:

1)多行变一行:

collect_list/collect_set列转行函数。

我们看对应的表结构为:

95413e93d67779d3d63156ee4105ace5.png

  使用collect_list之后为:

acb71eb81c6e461e329f79d4ed5d2aa3.png

  仔细看发现“李四”看了两遍“霸王别姬”,需要去重,因此,使用collect_set函数:

4f153f45480219d2825979b97f303e8d.png

这一功能的一个最大好处是突破了group by的限制,分组之后如果另一个字段有多个值且不在分组字段中,则需要用collect_set/collect_list函数。

ab5fd1463f139373683ecc07541b2242.png

以上为多行转一行,如何将一行转多行呢?

2)一行转多行:

我们有explode()和lateral view()函数:

explode():

该函数接收一个参数,参数类型需是array或者map类型,该函数的输出是把输入参数的每个元素拆成独立的一行记录。

select explode(split('123,3455,2568',','))

这个函数的一个问题在于无法将list/set等格式做转化,如果要做转化则需要将list/set转化成string,然后再用explode/split切分形成多行。

lateral view():

Lateral View 一般与用户自定义表生成函数(如explode())结合使用。UDTF为每个输入行生成零个或多个输出行,Lateral view首先将UDTF应用于基表的每一行,然后将结果输出行连接到输入行,已形成具有提供的表别名的虚拟表。

例如:

select  items_page, numfrom data_limitlateral view explode(items_pages) good as items_page

其中:

a)explode是将items_pages行变列;

b)good是虚拟表,用来存储explode之后的数据,并定义列名为items_page;

c)lateral view是将items_page与上面的字段num进行笛卡尔积,呈现出多列表示;

   跳开hive,我们可以通过pyspark实现多行变多列,如下:

data=spark.createDataFrame([[1,[2,9],3],[4,[5,6],6],[7,[8,0],9]],['a','b','c'])Length = len(data.select(‘b’).take(1)[0][0])data_t=data.select([data.b]+[data.b[i] for i in range(length)]).show()Print(data_t)

结果为:

14a5320b422b370737d2dec7ce9f7a39.png

   如上即为hive的一些常用函数,笔者做了简单汇总,希望对大家有所助益。

欢迎大家关注公众号:bcfc5b262205fe56d4767b8e68ae03b4.png来都来了,点个关注再走呗~

版权声明:本文为weixin_33489093原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。