一、自定义简单标准函数(UDF)
1.定义:
标准函数:指一行的一列或多列作为参数传入,返回单一值的函数。
如:to_date(string timestamp),sqrt(double a),concat(string a,string b)等。
2.实现自定义concat:
功能:用于将两个输入参数连接起来。
两个重载函数:myconcat(string str1, string str2) 和 myconcat(int a, int b),如下Java代码:
注1:UDF类在
hive-exec-2.3.4.jar
中;
注2:用
@Description()
可以给函数加使用说明;
注3:继承UDF类,实现evaluate()方法(支持重载)。
package com.hive.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
/*
* 自定义标准函数 ConcatUDF
*/
@Description(name = "myconcat",
value = "_FUNC_(str1, str2)",
extended = "Example:\n"
+ "SELECT _FUNC_(string str1, string str2) FROM src;\n"
+ "SELECT _FUNC_(int a, int b) FROM src;")
public class ConcatUDF extends UDF{
private Text text = new Text();
public Text evaluate(Text str1, Text str2) {
if (str1 == null || str2 == null) {
text.set("unvalid");
return text;
}
text.set(str1.toString() + "+" + str2.toString());
return text;
}
public String evaluate(Integer a, Integer b) {
if (a == null || b == null) {
return "unvalid";
}
return a + "+" + b;
}
}
3.关于evaluate()方法的输入输出参数类型的说明:
—引用自《Hive编程指南》
UDF中evaluate()函数的参数和返回值类型只能是Hive可以序列化的数据类型。例如,如果用户处理的全是数值,那么UDF的输出参数类型可以是基本数据类型int、Integer封装的对象或者是一个IntWritable对象,也就是Hadoop对整型封装后的对象。用户不需要特别的关心将调用到哪个类型,因为当类型不一致的时候,Hive会自动将类型转换成匹配的类型。需要记住的是,null在Hive中对于任何数据类型都是合法的,但是对于Java基本数据类型,不能是对象,也不能是null。
4.打包Jar file,并运行测试 :
1.将com.hive.udf包右键导出为 JAR file,命名为:"myUDF.jar";
2.利用Windows的cmd或者PowerShell(推荐)将JAR文件上传到Linux服务器
命令如下:(在JAR文件目录下执行)
> scp myUDF.jar root@remoteIP:~/myJars/hive/
(其中remoteIP为远程服务器IP)
3.启动hadoop,启动hive('hive>'下输入,仅支持全路径名)
> add jar /root/myJars/hive/myUDF.jar
<会提示成功加入 class path>
4.注册临时/永久函数
> create temporary function myconcat as 'com.hive.udf.ConcatUDF';(临时,作用本次会话)
> create function myconcat as 'com.hive.udf.ConcatUDF';(永久)
5.运行测试
> select myconcat('a','b'),
> myconcat(1,2);
a+b 1+2
<此即连接的结果>
> select myconcat(null,'b'),
> myconcat(null,2);
unvalid unvalid
<此即参数为null的处理结果>
5.Hive函数的几个操作命令:
1.Hive函数相关命令:
--显示所有包含'concat'子串的函数
> show functions like '*concat*';
--查看函数描述用法
> desc function concat;
--查看函数描述用法(详细)
> desc function extended concat;
--删除自定义函数
> drop temporary function myconcat;
二、自定义复杂标准函数(UDF)
1.实现自定义nvl:
功能:如果函数传入的参数是null,则返回默认值,否则返回传入值。函数需要两个参数,一个传入值,一个默认值。如果第一个参数为null,则返回第二个默认参数。
注1:(
回答了为什么要用GenericUDF实现 ?
)此功能当然也能用简单UDF实现,但是当面对不同输入参数类型时,就需要重载很多个不同的evaluate()方法,非常复杂;由此,复杂UDF的实现,可以解决这个问题,GenericUDF会以编程的方式检查输入的数据类型,然后做出反馈。
注2:需要继承GenericUDF类,实现initialize()、evaluate()、getDisplayString() 三个方法。
package com.hive.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDFUtils;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
@Description(name="mynvl",
value="_FUNC_(value,default_value) - Returns default value if value"
+ " is null else returns value",
extended="Examples:\n"
+ "> SELECT _FUNC_(null,'bla') FROM src LIMIT 1;\n")
public class GenericNvlUDF extends GenericUDF{
/*
* ObjectInspector:用来包装对各种输入参数类型的统一操作
*/
private ObjectInspector[] argumentOIs;
/*
* ReturnObjectInspectorResolver:用来确定方法最终返回值的类型
* --update()是用来获取新参数的类型,并判断是否与之前参数类型一致(会自动拆装包)
*/
private GenericUDFUtils.ReturnObjectInspectorResolver returnOIResolver;
/*
* initialize():输入参数类型检查
*/
@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
this.argumentOIs = arguments;
if (arguments.length != 2) {
throw new UDFArgumentLengthException("The operator 'NVL' accepts 2 arguments.");
}
//检查两个参数的数据类型是否匹配
this.returnOIResolver = new GenericUDFUtils.ReturnObjectInspectorResolver(true);
if (!(returnOIResolver.update(arguments[0]) && returnOIResolver.update(arguments[1]))) {
throw new UDFArgumentTypeException(2,"The 1st and 2nd args of funciton NVL should have"
+ "the same type, but they are different:\"" + arguments[0].getTypeName()
+ "\" and \"" + arguments[1].getTypeName() + "\"");
}
return returnOIResolver.get();
}
/*
* evaluate():输入参数处理逻辑
*/
@Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
//如果第一个参数为null,则返回第二个默认参数
//returnOIResolver.convertIfNecessary()对返回参数进一步转换类型
Object retVal = returnOIResolver.convertIfNecessary(arguments[0].get(), argumentOIs[0]);
if (retVal == null) {
retVal = returnOIResolver.convertIfNecessary(arguments[1].get(), argumentOIs[1]);
}
return retVal;
}
/*
* getDisplayString():用于Hadoop task内部,使用到此函数时显示调试信息
*/
@Override
public String getDisplayString(String[] children) {
StringBuilder sb = new StringBuilder();
sb.append("if ");
sb.append(children[0]);
sb.append(" is null ");
sb.append("returns");
sb.append(children[1]);
return sb.toString();
}
}
2.打包Jar file,并运行
1.将com.hive.udf包右键导出为 JAR file,命名为:"myUDF.jar";
2.利用Windows的cmd或者PowerShell(推荐)将JAR文件上传到Linux服务器
命令如下:(在JAR文件目录下执行)
> scp myUDF.jar root@remoteIP:~/myJars/hive/
(其中remoteIP为远程服务器IP)
3.启动hadoop,启动hive('hive>'下输入,仅支持全路径名)
> add jar /root/myJars/hive/myUDF.jar
<会提示成功加入 class path>
4.注册临时/永久函数
> create temporary function mynvl as 'com.hive.udf.GenericNvlUDF';(临时,作用本次会话)
> create function myconcat as 'com.hive.udf.GenericNvlUDF';(永久)
5.运行测试
> select mynvl('a','b'),
> mynvl(1,2),
> mynvl(array('a'),array('c','d')),
> mynvl(map('a',1),map('b',2,'c',3)),
> mynvl(struct('a',1),struct('b',2));
a 1 ["a"] {"a":1} {"col1":"a","col2":1}
<即:第一个参数非null时返回第一个参数,支持不同类型>
> select mynvl(null,'b'),
> mynvl(1,'a'),
> mynvl(null,array('c','d')),
> mynvl(null,map('b',2,'c',3)),
> mynvl(null,struct('b',2));
b 1 ["c","d"] {"b":2,"c":3} {"col1":"b","col2":2}
<即:第一个参数为null时返回第二个默认参数>
三、参考文章
2.《Hadoop海量数据处理:技术详解与项目实战》范东来 第6章 6.7.1 UDF
3.《Hive编程指南》第13章 13.7 UDF与GenericUDF