hive substr函数_数据分析工具篇——HQL函数及逻辑
本篇文章我們梳理一下hive常用的函數(shù),對(duì)于hive而言,常用的函數(shù)并不是特別多,往往記住關(guān)鍵幾個(gè),就可以解決80%的問題,這也是大家喜歡hive的原因,那么,常用的函數(shù)有哪些呢?
時(shí)間函數(shù)
1)時(shí)間格式轉(zhuǎn)化
常用的日期格式主要有兩個(gè):時(shí)間戳和日期格式,時(shí)間戳便于計(jì)算,日期格式便于閱讀,兩者各有利弊,所以需要相互轉(zhuǎn)換,轉(zhuǎn)換的對(duì)應(yīng)函數(shù)主要有:
from_unixtime(timestamp,dateformat):將時(shí)間戳轉(zhuǎn)化為日期格式,格式必須是10位,毫秒級(jí)的時(shí)間戳需要用cast轉(zhuǎn)化成秒級(jí)。unix_timestamp(date,dateformat):日期格式轉(zhuǎn)化為時(shí)間戳,如果括號(hào)內(nèi)沒有參數(shù)則表示返回當(dāng)前的時(shí)間戳。例如:
時(shí)間戳轉(zhuǎn)日期格式:
select from_unixtime(unix_timestamp(),'yyyy-MM-dd?HH:mm:ss')?as date_time, from_unixtime(1537924406,'yyyy-MM-dd')?as?date_time1from dual;如果時(shí)間戳是毫秒級(jí),則字段修改為:
from_unixtime(cast(unix_timestamp()/1000 as int),'yyyy-MM-dd HH:mm:ss') as date_time日期格式轉(zhuǎn)時(shí)間戳:
select unix_timestamp()?as?time_stamp,??--返回當(dāng)前時(shí)間戳; unix_timestamp('2018-09-26?9:13:26','yyyy-MM-ddHH:mm:ss')?as?time_stamp1from dual;作為一個(gè)數(shù)據(jù),在不同系統(tǒng)之間游走,有沒有經(jīng)常遇到時(shí)間格式不匹配的情況,所以需要做格式的轉(zhuǎn)化:
date_format(string/date,dateformate):把字符串或者日期轉(zhuǎn)成指定格式的日期。日期格式調(diào)整:
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有沒有發(fā)現(xiàn)一個(gè)問題,date_format只能從yyyy-MM-dd格式轉(zhuǎn)化成其他格式,對(duì)于其他類型,例如:yyyyMMdd,則不能處理,所以需要靈活一點(diǎn),將各個(gè)格式的時(shí)間轉(zhuǎn)化成時(shí)間戳,再通過時(shí)間戳轉(zhuǎn)化成其他格式:
select from_unixtime(unix_timestamp(substr('20191007000000',1,8),'yyyyMMdd'),'yyyy-MM-dd');如此一來,各種格式都可以轉(zhuǎn)化。
2)日期的加減差運(yùn)算:
日期的加減是對(duì)日期格式數(shù)據(jù)的基本運(yùn)算,常用的是時(shí)間差計(jì)算、加一天、減一天等。
時(shí)間差計(jì)算:
datediff(date,date1);日期差,即兩個(gè)日期差幾天。months_between(date,date1);兩個(gè)日期差幾個(gè)月,用法一致。select?datediff('2015-05-22','2015-05-29') as date_ff;?--[-7]在原日期上加n天:
date_add:在現(xiàn)在日期上增加天數(shù),
add_months是增加月份,用法一致。
select date_add('2015-05-22 15:34:23',2); --2015-05-24在原日期上減n天:
date_sub:在現(xiàn)在日期上減少天數(shù)。
select date_sub('2015-05-22 15:34:23',2); --2015-05-203)時(shí)間截取:
如果我們獲取到一個(gè)完整的時(shí)間,但是只想用其中的一部分,勢(shì)必牽扯到時(shí)間截取的功能,常見的時(shí)間截取主要是如下幾個(gè)函數(shù):
to_date:獲取完整時(shí)間中的日期部分:
select to_date('2015-06-01 15:34:23'); --2015-06-01year:獲取完整時(shí)間中的年份:
select year('2015-05-22 15:34:23'); --2015month:獲取完整時(shí)間中的月份:
select month('2015-05-22 15:34:23'); --5day:獲取完整時(shí)間中的日期:
select day('2015-05-22 15:34:23'); --224)日期中其他靈活操作:
???日期除了計(jì)算多少天,截取某一段,還會(huì)牽扯一些比較靈活的操作方法,例如:判斷當(dāng)天是周幾,判斷年周、月周,當(dāng)月的最后一天和第一天,下n天,上n天等。
???獲取一年中的第幾周:weekofyear:
select?weekofyear('2015-05-22?15:34:23');?--21獲取當(dāng)月中的第幾周:
select weekofyear('2015-05-22')-weekofyear(trunc('2015-05-22', ‘MM’))+1;這其中有一個(gè)有意思的點(diǎn):每一周是按照周一到周日為一個(gè)完整周,與英美的周日到周六的邏輯有些不一樣,所以在如下日歷中:
11月1日為周日,按照代碼中的邏輯與10月31日是一周。
判斷當(dāng)天為周幾:
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當(dāng)前日期下個(gè)星期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為對(duì)應(yīng)的日期寫法,可以作為上面簡(jiǎn)單羅列的補(bǔ)充版):
select?????day???????????????????????????????????????????????????????????????????????????????????????????????????--?時(shí)間????,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 -- 當(dāng)前日期的下個(gè)周二????,trunc(day,'MM')???????????????????????????????????????????????????????????????????as?month_first_day??--?當(dāng)月第一天 ,last_day(day) as month_last_day -- 當(dāng)月最后一天 ,to_date(concat(year(day),'-',lpad(ceil(month(day)/3) * 3 -2,2,0),'-01')) as season_first_day -- 當(dāng)季第一天????,last_day(to_date(concat(year(day),'-',lpad(ceil(month(day)/3)?*?3,2,0),'-01')))???as?season_last_day??--?當(dāng)季最后一天????,trunc(day,'YY')???????????????????????????????????????????????????????????????????as?year_first_day???--?當(dāng)年第一天 ,last_day(add_months(trunc(day,'YY'),12)) as year_last_day -- 當(dāng)年最后一天 ,weekofyear(day) as weekofyear -- 當(dāng)年第幾周 ,second(day) as second -- 秒鐘 ,minute(day) as minute -- 分鐘 ,hour(day) as hour -- 小時(shí)????,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;數(shù)據(jù)運(yùn)算總結(jié)
Hive中數(shù)據(jù)一般是可以用常規(guī)的運(yùn)算符號(hào)表示的,例如:“加減乘除”用“+-*/”表示,但是對(duì)于初次之外的運(yùn)算需要用到對(duì)應(yīng)的函數(shù):
sum():求和;count():求數(shù)據(jù)量;avg():求平均值;distinct:求不同值數(shù)(去重);min:求最小值;max:求最大值;pmod(int a, int b):返回a除以b的余數(shù)的絕對(duì)值;round:四舍五入?select?round(數(shù)值,小數(shù)點(diǎn)位數(shù));ceil:向上取整?select?ceil(45.6);?floor:向下取整 select floor(45.6);在數(shù)據(jù)運(yùn)算過程中經(jīng)常會(huì)遇到用字符串形式存儲(chǔ)的數(shù)值,計(jì)算過程中需要將其轉(zhuǎn)化成數(shù)值型,然后進(jìn)行運(yùn)算,其中運(yùn)算方法為:
cast(aaa?as?int):將string轉(zhuǎn)化成int;cast(aaa as decimal(10, 2)):將string轉(zhuǎn)化成float,保留兩位小數(shù);當(dāng)然也可以將數(shù)值轉(zhuǎn)化成字符串:
cast(aaa as string):將int轉(zhuǎn)化成string;除了上面這些方法外,hive在數(shù)據(jù)運(yùn)算方面還有大于、小于、等于等操作,這些操作都有對(duì)應(yīng)的運(yùn)算符號(hào)表示,在此就不多描述了。
字符串操作總結(jié)
字符串可以說是最常用的處理思路,在特征處理、數(shù)據(jù)清洗過程中高頻使用,這些函數(shù)用的好會(huì)為接下來數(shù)據(jù)建模、統(tǒng)計(jì)運(yùn)算帶來極大便利。下面我們來梳理一下常用的字符串處理方法:
1)空格處理:
trim(String?A):去除A兩側(cè)的空格;ltrim(String?A):去除左邊空格;rtrim(String?A):去除右邊空格select trim('abc') from lxw_dual;2)單個(gè)字符串操作:
reverse('abcde')=edcba:字符串反轉(zhuǎn)???lower:轉(zhuǎn)成小寫
select lower('Hive'); --hiveupper:轉(zhuǎn)成大寫
select lower('Hive'); --HIVElength:長(zhǎng)度
select length('Hive'); --4substr:求子串
select?substr('hive',2);?--iveselect substr('hive',2,1); --ilpad:左填充
對(duì)hive填充到10位,補(bǔ)位用#
select lpad('hive',10,'#'); --######hiverpad:右填充
select rpad('hive',10,'#'); --hive######regexp_extract():正則表達(dá)式處理
regexp_extract(string?subject,?string?pattern,?int?index); --函數(shù)的應(yīng)用;將字符串subject按照pattern正則表達(dá)式的規(guī)則拆分,返回index指定的字符:
select clientcode, regexp_extract(filterlist,'(filtertype"\\:")(\\d+)(",)',2)?as?filtertypefrom tmp_action_click在正則表達(dá)式中,經(jīng)常會(huì)用到貪心算法(.*?)和非貪心算法(.*)用法,用來清洗字符串中的內(nèi)容。
正則表達(dá)式替換函數(shù):
regexp_replace('foobar','oo|ar','')=fb #替換oo|ar為空字符有意思的是,hive中并沒有直接的replace函數(shù),如果是整個(gè)字符串的判斷可以用case when的方式操作,如果只替換其中一部分可以用regexp_replace操作。
get_json_object(string?json_string,string?path)該函數(shù)的第一個(gè)參數(shù)是json對(duì)象變量,第二個(gè)參數(shù)使用$表示json變量標(biāo)識(shí),然后用.或[]讀取對(duì)象或數(shù)組。
例如:
如果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"?????}對(duì)應(yīng)的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第二個(gè)常用的函數(shù)為:
json_tuple(string json_string,string k1,string k2...)該函數(shù)的第一個(gè)參數(shù)是json對(duì)象變量,之后的參數(shù)是不定長(zhǎng)參數(shù),是一組鍵k1,k2...,返回值是元組,該方法比get_json_object高效,因?yàn)榭梢栽谝淮握{(diào)用中輸入多個(gè)鍵值。
select src.timestamp, b.*from?src_json?srclateral view json_tuple(src.json, 'email', 'owner') b as f1, f2;???另外,單個(gè)字符串如果是url,可以直接使用pares_url函數(shù),個(gè)人感覺這個(gè)函數(shù)比較雞肋,看起來很有用,但是大部分功能可以用string拼接的方式搞定。
URL解析函數(shù):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應(yīng)用過程中還有一個(gè)k-v值的函數(shù):
str_to_map(String text,String delimiter1,String delimiter2)使用兩個(gè)分隔符將文本拆分成鍵值對(duì)。Delimiter1將文本分成k-v對(duì),Delimiter2分割每個(gè)k-v對(duì)。對(duì)于delimiter1的默認(rèn)值是',',delimiter2的默認(rèn)值是'='。
select?str_to_map('abc:11&bcd:22',?'&',?':')--[“abc”:“11”, “bcd”:“22”]3)多字符串操作:
?? concat():字符串拼接
concat('aa','bb','cc')=aabbcc?? concat_ws(sep,str1,str2,...):根據(jù)固定的分隔符連接后側(cè)字符串;
?? concat_ws第一個(gè)參數(shù)是其它參數(shù)的分隔符,分隔符的位置放在要連接的兩個(gè)字符串之間,分隔符可以是一個(gè)字符串,也可以是其他參數(shù)。
Select concat_ws(',','11','22','33'); --11,22,33split(str,regex):數(shù)據(jù)切分。
該函數(shù)第一個(gè)參數(shù)是字符串,第二個(gè)參數(shù)是設(shè)定的分隔符,通過第二個(gè)參數(shù)把第一個(gè)參數(shù)做拆分,返回一個(gè)數(shù)組:
select?split('123,3455,2568',',')--[“123”,”123”,”123”]字段的數(shù)據(jù)處理
特征處理過程中也會(huì)經(jīng)常遇到條件處理,例如:是否為空、滿足什么條件、排序等,我們簡(jiǎn)單做一下了解:
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,?...);--返回第一個(gè)不為null的value值。select?COALESCE(null,'aaa',50)from?default.dual?limit?1;--aaaselect?COALESCE(1,'aaa',50)from?default.dual?limit?1;--12)條件判斷:
select?if(boolean?testCondition,?T?valueTrue,?T?valueFalseOrNull);select?if(1=1,'bbbb',111);--if條件判斷表達(dá)式。3)row_number()函數(shù):分組排序的功能。
row_number() over(distribute by col1 sort by clo2 desc)字段的行列變化
Hive中比較有特色的功能是行列變化,可以實(shí)現(xiàn)多行變一行,也可以實(shí)現(xiàn)一行變多行,但是其中有個(gè)缺陷,就是多行變多列無法實(shí)現(xiàn),會(huì)用python的同學(xué)可以通過pyspark實(shí)現(xiàn)對(duì)應(yīng)的變化:
1)多行變一行:
collect_list/collect_set列轉(zhuǎn)行函數(shù)。
我們看對(duì)應(yīng)的表結(jié)構(gòu)為:
??使用collect_list之后為:
??仔細(xì)看發(fā)現(xiàn)“李四”看了兩遍“霸王別姬”,需要去重,因此,使用collect_set函數(shù):
這一功能的一個(gè)最大好處是突破了group by的限制,分組之后如果另一個(gè)字段有多個(gè)值且不在分組字段中,則需要用collect_set/collect_list函數(shù)。
以上為多行轉(zhuǎn)一行,如何將一行轉(zhuǎn)多行呢?
2)一行轉(zhuǎn)多行:
我們有explode()和lateral view()函數(shù):
explode():
該函數(shù)接收一個(gè)參數(shù),參數(shù)類型需是array或者map類型,該函數(shù)的輸出是把輸入?yún)?shù)的每個(gè)元素拆成獨(dú)立的一行記錄。
select explode(split('123,3455,2568',','))這個(gè)函數(shù)的一個(gè)問題在于無法將list/set等格式做轉(zhuǎn)化,如果要做轉(zhuǎn)化則需要將list/set轉(zhuǎn)化成string,然后再用explode/split切分形成多行。
lateral view():
Lateral View 一般與用戶自定義表生成函數(shù)(如explode())結(jié)合使用。UDTF為每個(gè)輸入行生成零個(gè)或多個(gè)輸出行,Lateral view首先將UDTF應(yīng)用于基表的每一行,然后將結(jié)果輸出行連接到輸入行,已形成具有提供的表別名的虛擬表。
例如:
select items_page,?numfrom?data_limitlateral view explode(items_pages) good as items_page其中:
a)explode是將items_pages行變列;
b)good是虛擬表,用來存儲(chǔ)explode之后的數(shù)據(jù),并定義列名為items_page;
c)lateral view是將items_page與上面的字段num進(jìn)行笛卡爾積,呈現(xiàn)出多列表示;
???跳開hive,我們可以通過pyspark實(shí)現(xiàn)多行變多列,如下:
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)結(jié)果為:
???如上即為hive的一些常用函數(shù),筆者做了簡(jiǎn)單匯總,希望對(duì)大家有所助益。
歡迎大家關(guān)注公眾號(hào):來都來了,點(diǎn)個(gè)關(guān)注再走唄~總結(jié)
以上是生活随笔為你收集整理的hive substr函数_数据分析工具篇——HQL函数及逻辑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql视图_mysql之视图详解
- 下一篇: java set类_java中set类型