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;