自定義函數UDF
雖然hive已經提供了足夠多的內置函數供我們使用,但是有時候需要自己去寫函數來處理業務數據。
以官方給的UDF例子來說明,代碼如下
創建一個將字符串轉換成小寫的函數,Lower
類需要繼承UDF
類,並在Lower
類定義訪問類型爲public
方法名爲evaluate
的函數
package com.utstar.patrick;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class Lower extends UDF {
public Text evaluate(final Text s) {
if (s == null) { return null; }
return new Text(s.toString().toLowerCase());
}
}
將寫的代碼打成一個jar包,假如jar名爲udf.jar
,並上傳到服務器目錄/usr/local/src/hive/jars
。
執行add jar /usr/local/src/hive/jars/udf.jar;
將jar文件放到分佈式緩存裏。
執行create temporary function my_lower as 'com.utstar.patrick.Lower';
創建臨時的function
然後就像使用內置方法一樣使用my_lower
方法。如select my_lower("ABc");
可以執行drop temporary function if exists my_lower;
來刪除臨時的function
關於org.apache.hadoop.hive.ql.exec.UDF
類更詳細的說明可以查看源碼
transform腳本
Hive的 TRANSFORM
關鍵字提供了在SQL中調用自寫腳本的功能,適合實現Hive中沒有的功能但是又不想寫UDF的情況。
關於TRANSFORM
首先需要知道的是,所有的column在作爲參數傳入用戶自定義的transform腳本時都會預先轉換成字符串並且用TAB
分隔。如果是NULL值的話會轉變成\N
字符串以區別空字符串,腳本里標準的輸出也是用TAB
分隔的字符串,如果是\N
字符串會被hive當做NULL處理。
如下一個簡單的字符串拼接腳本。這個腳本和HadoopStreaming腳本非常類似的,都是通過標準輸入和輸出來處理數據。
注意需要用strip()
來消除首尾的空格和換行符,否則打印時會有換行。
import sys
for line in sys.stdin:
a, b = line.strip().split("\t")
print " : ".join([a, b])
假如上述腳本文件保存爲 /usr/local/src/hive/jars/my.py
添加文件到分佈式緩存 add file /usr/local/src/hive/jars/my.py;
示例如下
select transform("name","patrick") using 'my.py' as str;
如下圖所示
我們以HIVE學習四:Window And Analytical Function中的orders表爲例將order_id
和customer_name
拼接起來
select transform(order_id, customer_name) using 'my.py' as str from orders;
如下圖所示
lateral view
在說明這個lateral view
,需要先了解下explode
函數。
如下所示,explode
函數的參數是array
,該函數會將array
轉變成多條數據。
用split
函數可以生成array
如圖 select split("1,2,3",",") as arr;
將字符串1,2,3
按照逗號分割成了一個array
然後通過 select explode(split("1,2,3",",")) as arr;
將一個array
變成了多條數據,成功地實現了行轉列。
在瞭解到explode
之後我們接着看Lateral View
其完整語法如下,一般都與UDTF函數如explode
聯合使用
lateralView: LATERAL VIEW udtf(expression) tableAlias AS columnAlias (',' columnAlias)*
fromClause: FROM baseTable (lateralView)*
假設我們有如下一張表pageads
,其中add_list
的類型是array<int>
create table pageads
(pageid string, adid_list array<int>);
初始化表pageads
insert into table pageAds
select "front_page", Array(1,2,3)
union
select "contact_page", Array(3,4,5);
查看錶數據
可以通過 show create table pageads;
和desc formatted pageads;
查看錶的具體信息
然後我們想查看每個廣告在哪個頁面上出現過,可以執行如下語句
select pageid, adid from pageads lateral view explode(adid_list) temp as adid;
如果想查看每個廣告出現的次數,就可以執行如下語句
select count(*), adid from pageads lateral view explode(adid_list) temp as adid group by adid;
Multiple Lateral Views
一個from
語句可以包含多個LATERAL VIEW
語句。後面的LATERAL VIEW
語句可以引用出現在LATERAL VIEW
左邊的表的任何字段。
例如,下面sql查詢是正確的
SELECT * FROM exampleTable
LATERAL VIEW explode(col1) myTable1 AS myCol1
LATERAL VIEW explode(col2) myTable2 AS myCol2;
創建如下表
create table test
(col1 array<int>, col2 array<string>);
初始化數據如下
insert into table test
select Array(1,2), Array("a","b","c")
union
select Array(3,4), Array("d","e","f");
查看數據如下圖
然後執行下面的sql語句,包含多個lateral view
SELECT * FROM test
LATERAL VIEW explode(col1) myTable1 AS myCol1
LATERAL VIEW explode(col2) myTable2 AS myCol2;
結果如下圖
一個包容萬象的小例子
假設有這麼一張表stu
create table stu
(name string, info string);
表中有如下數據
insert into table stu
select "zhangsan","math:90,english:60"
union
select "lisi","chinese:80,math:66,english:77"
union
select "wangwu","chinese:66,math:55,english:80";
如下圖所示
現在要查詢每門課程中最高分和其對應的學生姓名
第一步,首先explode
出學生、課程、分數的信息,sql如下
select transform(name, sub_grade) using 'split.py' as name,sub,grade
from stu lateral view explode(split(info,",")) temp as sub_grade
注意一點,transform
腳本需要處理name, sub_grade
,而不能像select name,transform(sub_grade) using 'split.py' as sub,grade from stu lateral view explode(split(info,",")) temp as sub_grade;
一樣,簡單地說你所返回的數據要全部從transform
腳本里輸出,而不能一部分從表引用,一部分從transform
腳本里輸出。
其中transform
腳本文件split.py
內容如下
import sys
for line in sys.stdin:
a, b = line.strip().split("\t")
sub, grade = b.split(":")
print "{}\t{}\t{}".format(a, sub, grade)
第二步,由於用transform
腳本做了transformation
處理,類型全部會變成string類型,具體可參考Setting Types for Sort By這篇我寫的博客,需要用cast
函數將grade
轉化爲int
類型。
select name, sub, cast(grade as int) grade from
(
select transform(name, sub_grade) using 'split.py' as name,sub,grade from stu lateral view explode(split(info,",")) temp as sub_grade
) a
第三步,用窗口函數獲取每個課程的最高分及學生姓名
select *,first_value(grade) over (partition by sub order by grade desc) as g
,first_value(name) over (partition by sub order by grade desc) as n
from
(
select name, sub, cast(grade as int) grade from
(
select transform(name, sub_grade) using 'split.py' as name,sub,grade from stu lateral view explode(split(info,",")) temp as sub_grade
) a
) b
第四步,去重即可完成最終的sql
select distinct sub,first_value(grade) over (partition by sub order by grade desc) as g
,first_value(name) over (partition by sub order by grade desc) as n
from
(
select name, sub, cast(grade as int) grade from
(
select transform(name, sub_grade) using 'split.py' as name,sub,grade from stu lateral view explode(split(info,",")) temp as sub_grade
) a
) b
參考網址
HivePlugins
LanguageManual+Transform
LanguageManual+Commands
LanguageManual+UDF
LanguageManualDDL-CreateFunction
UDF簡單使用
how-can-select-a-column-and-do-a-transform-in-hive