Hive QL用户自定义函数
Hive虽然具有丰富的内置函数,但这些内置函数并满足不了所有的业务需求。
在Hive中,除了Hive自带的内置函数之外,用户还可以根据需要自行定义函数。用户自定义函数(UDF)是一个允许用户扩展HQL的强大的功能。
Hive支持以下三种自定义函数:
- UDF:User-Defined-Function,用户自定义函数,一对一的输入输出。(最常用的)
- UDTF:User-Defined Table-Generating Functions,用户自定义表生成函数。一对多的输入输出,比如 lateral view explore()。
- UDAF:User-Defined Aggregation Function,用户自定义聚合函数,多进一出,比如 count/max/min。
创建自定义函数步骤
用户可以使用Java编写自己的UDF,一旦将用户自定义函数加入到用户会话中(交互式的或者通过脚本执行的),它们就将和内置的函数一样使用。
创建用户自定义函数的步骤如下:
- 编写自定义函数代码;
- 编译部署;
- 在hive中注册自定义函数;
- 在查询中使用自定义函数;
- 销毁自定义函数;
1. 用户自定义函数 UDF
UDF函数作用于单个数据行,且产生一个数据行作为输出。
定义UDF函数要注意下面几点:
- 继承org.apache.hadoop.hive.ql.exec.UDF;
- 重写evaluate(),这个方法不是由接口定义的,因为它可接受的参数的个数,数据类型都是不确定的。Hive会检查UDF,看能否找到和函数调用相匹配的evaluate()方法。
例如,下面我们定义一个字符串大小写转换的UDF:
public class ToLowerOrUpperCase extends UDF {
public String evaluate(String val, int i) {
if (StringUtils.isBlank(val)) {
return "";
}
else if (i == 0) {
return val.toUpperCase();
}
else {
return val.toLowerCase();
}
}
}
1)将编写的自定义函数源码编译并打包,例如udf.jar。
2)在hive中,将udf.jar包加载到classpath:
hive> add jar /hivedata/udf.jar;
3)将自定义函数注册为HIVE函数,例如取名为my_case:
hive> create temporary function my_case as 'hive_udf.ToLowerOrUpperCase';
4)在Hive QL查询中就可以应用自定义函数my_case:
hive> select my_case('abac',0);
2. 用户自定义表函数 UDTF
UDTF函数作用于单个数据行,且产生多个数据行作为输出,是一对多的输入输出。
定义UDTF函数要注意下面几点:
- 继承继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
- 重写initlizer()、process()、evaluate()方法。
例如,下面我们定义一个explode(Map(k,v))转换的UDTF(自定义表函数):
public class MyMapUDTF extends GenericUDTF{
@Override
public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException {
if (args.length != 1) {
throw new UDFArgumentLengthException("请传入要解析的列值。");
}
ArrayList<String> fieldNameList = new ArrayList<String>();
ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>();
fieldNameList.add("k"); // 字段"k"
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
fieldNameList.add("v"); // 字段"v"
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNameList,fieldOIs);
}
@Override
public void process(Object[] args) throws HiveException {
String input = args[0].toString();
String[] kv = input.split(";");
for(int i=0; i<kv.length; i++) {
try {
String[] result = kv[i].split(":");
forward(result);
} catch (Exception e) {
continue;
}
}
}
}
1)将编写的自定义函数源码编译并打包,例如udtf.jar。
2)在hive中,将udtf.jar包加载到classpath:
hive> add jar /hivedata/udtf.jar;
3)将自定义函数注册为HIVE函数,例如取名为my_map:
hive> create temporary function my_map as 'hive_udf.MyMapUDTF';
4)在Hive QL查询中就可以应用自定义函数my_map:
hive> select my_map("name:张三;age:23;gender:男");
3. 用户自定义聚合函数 UDAF
用户自定义聚合函数UDAF 接受多个输入数据行,并产生一个输出数据行。它实现多对一的输入输出,类似count、sum、max等聚合函数。
定义UDAF函数必须继承自org.apache.hadoop.hive.ql.exec.UDAF,并且包含一个实现了org.apache.hadoop.hive.ql.exec.UDAFEvaluator接口的的静态嵌套类。该嵌套类必须实现下面这5个方法:
| 方法 | 说明 |
|---|---|
| init() | 该方法负责初始化计算函数并重设它的内部状态 。 |
| iterate() | 每次对一个新值进行聚合计算时会调用该方法。 |
| terminatePartial() | Hive需要部分聚合结果时会调用该方法。 |
| merge() | Hive需要将部分聚合结果和另外部分聚合结果合并时会调用该方法。 |
| terminate() | 调用该方法返回最终聚合结果。 |
例如,下面我们定义一个计算最大值的UDAF(自定义聚合函数):
public class MyMaxUDAF extends UDAF {
public static class MaxIntUDAFEvaluator implements UDAFEvaluator {
private IntWritable result;
public void init() {
result = null;
}
public boolean iterate(IntWritable value) {
if (value == null) {
return true;
}
if (result == null) {
result = new IntWritable( value.get() );
} else {
result.set( Math.max( result.get(), value.get() ) );
}
return true;
}
public IntWritable terminatePartial() {
return result;
}
public boolean merge(IntWritable other) {
return iterate( other );
}
public IntWritable terminate() {
return result;
}
}
}
1)将编写的自定义函数源码编译并打包,例如udaf.jar。
2)在hive中,将udaf.jar包加载到classpath:
hive> add jar /hivedata/udaf.jar;
3)将自定义函数注册为HIVE函数,例如取名为my_max:
hive> create temporary function my_max as 'hive_udf.MyMaxUDAF';
4)在Hive QL查询中就可以应用自定义函数my_max:
hive> select my_max(age) from employees;
删除自定义函数
如果不再需要自定义函数了,可使用如下语法删除:
drop [temporary] function [if exists] [dbname.]function_name;
例如,要删除注册的自定义函数my_max,可以执行如下Hive QL命令:
hive> drop temporary function if exists my_max;