Hive基礎九---Hive函數--自定義函數UDF

版權聲明:本文爲博主原創文章,轉載請註明出處。

交流QQ: 824203453

歡迎訪問博主個人主頁:http://www.oldsheep.cn 

 

 

hive函數使用

系統自帶的函數

1)查看系統自帶的函數

hive> show functions;

2)顯示自帶的函數的用法

hive> desc function upper;

3)詳細顯示自帶的函數的用法

hive> desc function extended upper;

 

測試函數小技巧:

   直接用常量來測試函數即可

select substr("abcdefg",1,3);

而且,可以將hive的本地運行自動模式開啓:

hive>set hive.exec.mode.local.auto=true;

 

HIVE 的所有函數手冊:

https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF#LanguageManualUDF-Built-inTable-GeneratingFunctions(UDTF)

轉換函數

類型轉select cast("5" as int) ;

select cast("2017-08-03" as date) ;
select cast(current_timestamp as date);

1

1995-05-05 13:30:59

1200.3

2

1994-04-05 13:30:59

2200

3

1996-06-01 12:20:30

80000.5

create table t_fun(id string,birthday string,salary string)
row format delimited fields terminated by ',';

查詢:

select id,cast(birthday as date) as bir,cast(salary as float) from t_fun;

 

數學運算函數

select round(5.4);   ## 5  四捨五入

select round(5.1345,3) ;  ##5.135

select ceil(5.4) ; // select ceiling(5.4) ;   ## 6  向上取整

select floor(5.4);  ## 5  向下取整

select abs(-5.4) ;  ## 5.4  絕對值

select greatest(id1,id2,id3) ;  ## 6  單行函數

select least(3,5,6) ;  ##求多個輸入參數中的最小值

 

示例:

有表如下:

select greatest(cast(s1 as double),cast(s2 as double),cast(s3 as double)) from t_fun2;

結果:

+---------+--+

|   _c0   |

+---------+--+

| 2000.0  |

| 9800.0  |

+---------+--+

select max(age) from t_person group by ..;    分組聚合函數
select min(age) from t_person group by...;    分組聚合函數

 

字符串函數

substr(string str, int start)   ## 截取子串

substring(string str, int start)

示例:

select substr("abcdefg",2) ;

substr(string, int start, int len)

substring(string, int start, int len)

示例:

select substr("abcdefg",2,3) ;  ## bcd

 

concat(string A, string B...)  ## 拼接字符串

concat_ws(string SEP, string A, string B...)

示例:

select concat("ab","xy") ;  ## abxy
select concat_ws(".","192","168","33","44") ; ## 192.168.33.44

 

length(string A)

示例:

select length("192.168.33.44");  ## 13

 

split(string str, string pat)  ## 切分字符串,返回數組

示例:select split("192.168.33.44",".") ; 錯誤的,因爲.號是正則語法中的特定字符

select split("192.168.33.44","\\.") ;

upper(string str)  ##轉大寫

lower(string str)  ##轉小寫

 

時間函數

select current_timestamp; ## 返回值類型:timestamp,獲取當前的時間戳(詳細時間信息)

select current_date;   ## 返回值類型:date,獲取當前的日期

 

## 字符串格式轉unix時間戳——unix_timestamp:返回值是一個長整數類型

 

## 如果不帶參數,取當前時間的秒數時間戳long--(距離格林威治時間1970-1-1 0:0:0秒的差距)

select unix_timestamp();

 

unix_timestamp(string date, string pattern)

示例:

select unix_timestamp("2017-08-10 17:50:30");
select unix_timestamp("2017-08-10 17:50:30","yyyy-MM-dd HH:mm:ss");

## unix時間戳轉字符串格式——from_unixtime

from_unixtime(bigint unixtime[, string format])

示例:

select from_unixtime(unix_timestamp());
select from_unixtime(unix_timestamp(),"yyyy/MM/dd HH:mm:ss");

## 將字符串轉成日期date

select to_date("2017-09-17 16:58:32");

求年:

select year("2017-09-17 16:58:32");

求月份:

select month("2017-09-17 16:58:32");
hive (test) > select datediff("2018-10-10","2018-10-01");

OK

_c0

9

Time taken: 0.056 seconds, Fetched: 1 row(s)

hive (test)> select date_add("2018-10-10",3);

OK

_c0

2018-10-13

 

時間函數大全:

select

current_date,

current_timestamp,

unix_timestamp(current_timestamp),

unix_timestamp(current_timestamp,'yyyy-MM-dd'),

from_unixtime(1550545559,'yyyy-MM-dd hh'),

from_unixtime(1550545559,'yyyy-MM-dd hh:mm:ss'),

year(current_timestamp),

month(current_timestamp),

day(current_timestamp),

weekofyear(current_timestamp),

dayofmonth(current_timestamp),

hour(current_timestamp),

minute(current_timestamp),

second(current_timestamp),

date_add(current_date,1),

date_add(current_date,-1),

add_months(current_date,1),

add_months(current_date,-1),

last_Day(current_date),

next_Day(current_date ,'TU'),

date_add(current_date,-1),

datediff(current_date,'2019-03-08')

 

條件控制函數

IF

select id,if(age>25,'working','worked') from t_user;
select moive_name,if(array_contains(actors,'吳剛'),'好電影',’爛片兒’) from t_movie;

 

case when

語法:

CASE   [ expression ]

       WHEN condition1 THEN result1

       WHEN condition2 THEN result2

       ...

       WHEN conditionn THEN resultn

       ELSE result

END

case when有兩種用法:簡單匹配和條件匹配:

簡單匹配

case 字段  when  值1  then  返回值1

                 when  值2  then  返回值2

                 else  返回值3

end as 別名

條件匹配

case  when  表達式1  then  返回值1

when  表達式2  then  返回值2

         else  返回值3

end as 別名

 

有如下數據:

1,zhangsan,18,beijing,20000

2,lisi,28,shanghai,1500

3,wangwu,38,wuhan,4000

4,zhaoliu,35,changsha,10050

5,tianqi,23,shijiazhuang,30000

6,wangba,45,qinhuangdao,28000

7,wushuai,55,haerbin,80000

 

查詢:

把年齡和收入都用階段表示

年齡 ----> 少年/中年等

收入 -----> 屌絲/還行/oldsheep/土豪

 

建表:

create table t10(id int,name string,age int,addr string,income int)

row format delimited fields terminated by ',';

 

查詢:

select id,name,

case

when age<=20 then '少年'

when age>20 and age<=40 then '青年'

when age>40 and age<=60 then '中年'

when age>60 then '老年'

end as status,

addr,

income,

case

when income<=5000 then '窮屌絲'

when income>5000 and income<=15000 then '富屌絲'

when income>15000 and income<=30000 then '還行'

when income>30000 and income<=60000 then 'oldsheep'

when income>60000 then '土豪'

end as level

from t10;

 

結果:

+-----+-----------+---------+---------------+---------+--------+--+

| id  |   name    | status  |     addr      | income  | level  |

+-----+-----------+---------+---------------+---------+--------+--+

| 1   | zhangsan  | 少年      | beijing       | 20000   | 還行     |

| 2   | lisi      | 青年      | shanghai      | 1500    | 窮屌絲    |

| 3   | wangwu    | 青年      | wuhan         | 4000    | 窮屌絲    |

| 4   | zhaoliu   | 青年      | changsha      | 10050   | 富屌絲    |

| 5   | tianqi    | 青年      | shijiazhuang  | 30000   | 還行     |

| 6   | wangba    | 中年      | qinhuangdao   | 28000   | 還行     |

| 7   | wushuai   | 中年      | haerbin       | 80000   | 土豪     |

+-----+-----------+---------+---------------+---------+--------+--+

集合函數

array(3,5,8,9)   構造一個整數數組

array(‘hello’,’moto’,’semense’,’chuizi’,’xiaolajiao’)   構造一個字符串數組

array_contains(Array<T>, value)  返回boolean值

示例:

select moive_name,array_contains(actors,'吳剛') from t_movie;

select array_contains(array('a','b','c'),'c') ;

 

sort_array(Array<T>) 返回排序後的數組

示例:

select sort_array(array('c','b','a')) ;

select 'haha',sort_array(array('c','b','a')) as xx from (select 0) tmp;

 

size(Array<T>)  返回一個集合的長度,int值

示例:

select moive_name,size(actors) as actor_number from t_movie;

 

size(Map<K.V>)  返回一個imap的元素個數,int值

size(array<T>)   返回一個數組的長度,int值

 

map_keys(Map<K.V>)  返回一個map字段的所有key,結果類型爲:數組

map_values(Map<K.V>) 返回一個map字段的所有value,結果類型爲:數組

 

常見分組聚合函數

sum(字段)  :  求這個字段在一個組中的所有值的和

avg(字段)  : 求這個字段在一個組中的所有值的平均值

max(字段)  :求這個字段在一個組中的所有值的最大值

min(字段)  :求這個字段在一個組中的所有值的最小值

 

count():求一個組中的滿足某條件的數據條數!

 

舉例說明:

1,mary,female,jiuye

2,tom,male,meijiuye

3,kitty,female,meijiuye

4,white,male,jiuye

5,jack,male,jiuye

6,rose,female,meijiuye

建表語句:

create table t_count(id int,name string,gender string,job string)

row format delimited fields terminated by ',';

 

請求出,男生和女生中分別已就業的人數

方式1 :在count計數時進行判斷是否需要計入

select sex,count(if(job='jiuye',1,null))

from  t11

group by sex;

方式3:在count計數時判斷是否需要計入:

select sex,count(case when job='jiuye' then 1 else null end)

from  t11

group by sex;

 

方式3:先過濾掉不需要計入的數據,再分組計數

select sex,count(1)

from

(

select *

from t11

where job='jiuye') o1

group by o1.sex;

 

 

collect_set() :將某個字段在一組中的所有值形成一個集合(數組)返回

舉例:

有數據如下:

1,zhangsan,數學

1,zhangsan,化學

1,zhangsan,語文

1,zhangsan,搭訕學

2,lisi,數學

2,lisi,化學

2,lisi,聊騷

2,lisi,成人搏鬥學

3,wangwu,防狼術

3,wangwu,跆拳道

需求:

查詢出如下結果:

1

zhangsan

數學 化學  語文  搭訕學

2

lisi

數學 化學  聊騷

.....

....

....

 

創建表:

create table t13(id int,name string,subject string)

row format delimited fields terminated by ',';

 

加載數據:

load data local inpath '/root/hivedata/collectset.dat' into table t13;

 

select id,name,collect_set(subject)

from t13

group by id,name;

查詢結果如下:

+-----+-----------+------------------------------------------------------------+--+

| id  |   name      |            _c2                 |

+-----+-----------+--------------------------------------------------------------+--+

| 1   | zhangsan     | ["數學","化學","語文","街頭搭訕學"]  |

| 2   | lisi          | ["數學","化學","聊騷","成人搏鬥學"]  |

| 3   | wangwu     | ["防狼術","跆拳道"]                 |

+-----+-----------+----------------------------------------------------------------+--+

 

disctinct

數據:

1,zhangsan,28,beijing

1,lisi,29,shanghai

1,wangwu,30,beijing

2,zhaoliu,18,tianjin

2,tianqi,19,shenzhen

3,wangba,21,shenzhen

 

建表:

create table t_distinct(id int,

name string,

age int,

addr string)

row format delimited fields terminated by ',';

 

加載數據:

load data local inpath '/root/hivedata/distinct.dat' into table t_distinct;

 

表生成函數

表生成函數 行轉列函數:explode()

假如有以下數據,把所有的學科轉換成列數據。

1,zhangsan,化學:物理:數學:語文

2,lisi,化學:數學:生物:生理:衛生

3,wangwu,化學:語文:英語:體育:生物

映射成一張表:

create table t_stu_subject(id int,name string,subjects array<string>)

row format delimited fields terminated by ','

collection items terminated by ':';

 

加載數據:

load data local inpath '/root/hivedata/explode.dat' into table t_stu_subject;

 

使用explode()對數組字段“炸裂”

 

可以利用這個explode的結果,求去重的課程:

select distinct tmp.sub

from

(select explode(subjects) as sub from t_stu_subject) tmp;

 

表生成函數lateral view

雖然炸裂函數可以使用把行轉成列了,但是生成的結果數據中不能包含原有的id,name字段。

想實現原樣恢復的結果(結果如下圖所示),可以使用lateral view來實現。

select id,name,tmp.sub

from t_stu_subject lateral view explode(subjects) tmp as sub;

查詢結果:

理解: lateral view 相當於兩個表在join

左表:是原表

右表:是explode(某個集合字段)之後產生的表

而且:這個join只在同一行的數據間進行

 

如此,便做更多的查詢:

比如,查詢選修了生物課的同學

select a.id,a.name,a.sub from

(select id,name,tmp.sub as sub from t_stu_subject lateral view explode(subjects) tmp as sub) a

where sub='生物';

 

json解析函數

需求:有如下json格式的電影評分數據:

{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}

{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}

{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}

{"movie":"3408","rate":"4","timeStamp":"978300275","uid":"1"}

{"movie":"2355","rate":"5","timeStamp":"978824291","uid":"1"}

{"movie":"1197","rate":"3","timeStamp":"978302268","uid":"1"}

 

需要做各種統計分析。

發現,直接對json做sql查詢不方便,需要將json數據解析成普通的結構化數據表。可以採用hive中內置的json_tuple()函數

 

實現步驟:

  1. 創建一個原始表用來對應原始的json數據

create table t_json(json string);

注意: json數據不需要切分,所以不需要指定分隔符。

 

加載數據:

load data local inpath '/root/hivedata/ratings.data' into table t_json;

 

  1. 利用json_tuple進行json數據解析

測試,示例:

select json_tuple(json,'movie','rate','timeStamp','uid') as(movie,rate,ts,uid) from t_json limit 10;

產生結果:

 

窗口分析函數:

row_number() over()

語法: row_number()爲數據加行號。 over中寫劃分窗口的條件,比如怎麼分組怎麼排序等。

 

需求:需要查詢出每種性別中年齡最大的2條數據

數據如下:

1,18,a,male

2,19,b,male

3,22,c,female

4,16,d,female

5,30,e,renyao

6,26,f,female

7,16,d,renyao

8,30,e,renyao

9,26,f,female

10,14,b,male

 

創建表:

create table t_rn(id int,age int,name string,sex string)

row format delimited fields terminated by ',';

 

使用row_number函數,對錶中的數據按照性別分組,按照年齡倒序排序並進行標記

 

hql代碼:

select id,age,name,sex,

row_number() over(partition by sex order by age desc) as rank from t_rownumber;

產生結果:

然後,利用上面的結果,查詢出rank<=2的即爲最終需求

select id,age,name,sex

from

(select id,age,name,sex,

row_number() over(partition by sex order by age desc) as rank

from t_rownumber) tmp

where rank<=2;

sum() over()

sum over常用於 級聯求和

語法: sum(求和的字段)  over (窗口的生成的條件,及行的起始位置)

 

需求:

有一張每個店鋪的銷量紀錄表。

數據字段爲:店鋪名稱,月份,銷售額。

a,01,10
a,01,20
a,02,100

要求 統計每一個店鋪的月份總銷量,及累積到當前月份的總銷量。

實現:使用sum()over()窗口分析函數來實現

 

步驟1:先求出每家店鋪每個月的總金額

create table t_tmp as

select name,month,sum(sale) as amt from shop group by name,month;

+-------+--------+-------+--+

| name  | month  |  amt  |

+-------+--------+-------+--+

| a     | 01     | 350   |

| a     | 02     | 5000  |

| a     | 03     | 600   |

| b     | 01     | 7800  |

| b     | 02     | 2500  |

| c     | 01     | 470   |

| c     | 02     | 630   |

+-------+--------+-------+--+

 

步驟2:實現級聯求和

select name,month,amt,

sum(amt) over(partition by name order by month rows between unbounded preceding and current row) as accumulate

from t_tmp;

sum over中的 累加的語法格式:

重要提示

sum()over()的累加範圍指定語法:

 

sum() over(partition by x order by y rows between 8 preceding and current row)

sum() over(partition by x order by y rows between 8 preceding and 5 following)

sum() over(partition by x order by y rows between unbounded preceding and 5 following)

sum() over(partition by x order by y rows between unbounded preceding and unbounded following)

sum over需要指定累加的範圍,起始行 和終止行的位置。

起始位置和終止位置:

窗口內的第一行:unbounded preceding

窗口內當前行的前3行:3 preceding

當前行: current row

窗口中的最後一行   unbounded following

當前行的下一行  1 following

 

自定義函數

1)Hive 自帶了一些函數,比如:max/min等,但是數量有限,自己可以通過自定義UDF來方便的擴展。

2)當Hive提供的內置函數無法滿足你的業務處理需要時,此時就可以考慮使用用戶自定義函數(UDF:user-defined function)。

3)根據用戶自定義函數類別分爲以下三種:

       (1)UDF(User-Defined-Function)

              一進一出

       (2)UDAF(User-Defined Aggregation Function)

              聚集函數,多進一出

              類似於:count/max/min

       (3)UDTF(User-Defined Table-Generating Functions)

              一進多出

              如lateral view explore()

4)官方文檔地址

https://cwiki.apache.org/confluence/display/Hive/HivePlugins

 

需求:

有如下數據:

id,salay,bonus,subsidy

a,100,50,120

b,220,150,20

c,220,450,220

3個字段分別表示: 用戶id,基本工資,業績提成,股權收益

需要查詢出每個人的三類收益中最高的是哪一種收益。

求的不是最高收益,而是最高收益的類型

a,100,50,120,subsidy

b,220,150,20,salary

c,220,450,220,bonus

hive中有一個函數greatest(f1,f2,f3)可以求出n個字段中的最大值,但滿足不了本案例的需求。此時,我們可以考慮自己開發一個hive的函數(hive具備這個機制)

 

創建表:

create table t19(id int, salary int,bonus int,subsidy int)

row format delimited fields terminated by ',';

通過需求分析,如果有如下函數:

select id,f_c(salary,bonus,subsidy) from t19

f_c(salary,bonus,subsidy) :能夠接收3個整數,返回哪一個是最大的,就很容易實現需求;

可惜這樣的函數在hive中沒有;

解決方案:自定義一個這樣的函數;

 

實現思路:

hive的函數無非也就是一個邏輯的封裝,可以接收參數,返回結果,跟java中的方法本質上沒有區別。

hive就允許用戶開發一個java的方法,來實現你想要的函數的功能;

然後在hive中定義一個自己命名的函數,並將這個函數跟你的java方法所在的類關聯起來即可。

實現的流程:

  1. 創建javase 工程
  2. 導入hive的依賴jar包
  3. 自定義類 ,繼承父類UDF 重寫方法
  4. evaluate方法的業務邏輯實現
  5. 打成jar包,提交到hive運行的環境中
  6. 註冊臨時函數
  7. 使用註冊的函數查詢

 

實現步驟:

  1. 創建java工程,
  2. 導入hive的所有的java包

 

3,開發一個java類繼承(HIVE的父類UDF),重載一個方法: evaluate()

方法的功能:

輸入:3個整數值

返回:最大值所在的序號(然後使用case when來進行匹配)

public class HighestIncomType extends UDF{

     // 重載evaluate方法,而且必須是public

     public int evaluate(int a,int b,int c) {

      // 最簡單的一行代碼

return (a >b && a> c)?0:(b>c ? 1:2);

     }

}

 

4, 將java工程打成jar包,上傳到hive所在的機器上

 

5, 在hive的提示符中,將jar包添加到hive的運行時classpath

6, 註冊臨時函數

在hive的提示中,用hive語法聲明一個自定義函數,並與jar包中的java類關聯

hive (default)> add jar /root/myudf.jar ;

Added [/root/myudf.jar] to class path

Added resources: [/root/myudf.jar]

hive (default)>  create temporary function f_c as 'cn.huige.hiveudf.MyUDF';

OK

Time taken: 0.7 seconds

 

7. 就可以在sql查詢中使用這個臨時函數了

select id,

case

when type=0 then '基本工資'

when type=1 then '股權收益'

else '津貼'

end as type

from

(select id,f_c(salary,bonus,subsidy) as type from t19) o1;

 

自定義函數永久生效

上述創建的函數是臨時生效的,當hive窗口退出之後,函數就不能使用了。

如果需要創建的函數是永久生效的。就需要做一些其他的配置。

 

  1. 把自定義函數的jar包,放到hive的lib目錄下。
[root@hdp-03 ~]# cp myudf.jar apps/apache-hive-1.2.2-bin/lib/
  1. 註冊永久的函數
hive (default)>  create function f_c as 'cn.huige.hiveudf.MyUDF';

 

版權聲明:本文爲博主原創文章,轉載請註明出處。

交流QQ: 824203453

歡迎訪問博主個人主頁:http://www.oldsheep.cn 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章