为什么需要自定义函数
hive的内置函数满足不了所有的业务需求。
hive提供很多的模块可以自定义功能,比如:自定义函数、serde、输入输出格式等 。
常见自定义函数UDF分三种:
-
-
- UDF(User Defined Function),一进一出(输入一行,输出一行),比如:upper(),lowser()等。
- UDFA(User Defined Aggregation Funcation),多进一出(输入多行,输出一行),比如avg()、sum()、max()、min()、count()等。
- UDTF(User Defined Table Generating Functions),一进多出(输入一行,输出多行),比如collect_set()、collect_list()等。
-
UDF格式
-
-
-
- 继承org.apache.hadoop.hive.ql.exec.UDF;
- 重写evaluate()方法;
- 打包jar;
- 添加jar包
- 使用自定义函数
-
-
在新建的Maven工程的pom.xml,加入以下hive的依赖包,如下:
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.2</version>
</dependency>
定义UDF函数要注意下面几点:
- 继承
org.apache.hadoop.hive.ql.exec.UDF
- 重写evaluate(),这个方法不是有接口定义的,因为它可接受的参数的个数,数据类型都是不确定的。Hive会检查UDF,看能否找到和函数调用相配的evaluate()方法。
- 自定义函数第一个案例
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.UDF;
public class FirstUDF extends UDF {
public String evaluate(String str){
String upper=null;
//1、检查输入参数
if (StringUtils.isEmpty(str)){
} else {
// 转大写。
upper = str.toUpperCase();
}
return upper;
}
}
- 打包jar
- 添加jar包
- 把生成的jar包上传到服务器上的:/UDF.jar
- 命令加载
add jar /UDF.jar;
-- 创建一个临时函数名,要跟上面hive在同一个session里面:
create temporary function toUP as 'FirstUDF';
-- 查看
show functions;
-- 测试
select toUP("abcdef");
-- 删除函数
drop temporary function if exists toUP;
UDTF格式
-
-
- 继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
- 实现initialize()、process()、close()方法。
- UDTG首先会调用initialize()方法,此方法返回UDTF的返回行的信息(返回个数,类型)。
- 初始化完成后悔调用process()方法,对传入的参数进行处理,可以通过forward()方法把结果返回。
- 最后调用close()对需要清理的方法进行清理。
- 案例:使用UDTF对"Key:Value"这种字符串进行切分,返回结果为key,Value两个字段。
-
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import java.util.ArrayList;
import java.util.List;
public class ParseMapUDTF extends GenericUDTF {
//在initializez中初始化要输出字段的名称和类型
@Override
public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
//定义要输出列的名字的List,并且添加要输出的列名
List<String> structFieldNames = new ArrayList<>();
structFieldNames.add("key");
structFieldNames.add("value");
// 定义要输出列的类型的List,并且添加要输出列的类型
List<ObjectInspector> objectInspectorList = new ArrayList<>();
objectInspectorList.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
objectInspectorList.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, objectInspectorList); }
@Override
public void process(Object[] args) throws HiveException {
String strings=args[0].toString();
String[] split=strings.split(";");
for (String s : split) {
String [] kvArray=s.split(":");
forward(kvArray);
}
}
@Override
public void close() throws HiveException {
}
}
-
- 打包jar
- 添加jar包
-- UDTF格式
add jar /hive_init/UDTF.jar;
-- 创建一个临时函数parseMap
create temporary function parseMap as 'ParseMapUDTF';
show functions ;
-- 测试函数
select parseMap("name:zhang;age:30;address:shenzhen");
- UDAF格式
hive的udaf是自定义聚合函数配合group by使用,接受0行到多行数据返回一个计算结果值,定义今天内部表类,实现UDAFEvaluator的
- init()初始化一般肤质初始化内部字段,通常初始化用来存放最终结果的变量。
- iterate()每次都会对一个新的值进行聚合计算时时都调用该方法,一般会根据计算结果更新用来存放最终结果的变量,如果计算正确或者输入值合法就返回true;
- terminatePartial()这个方法直译过来就是“终止部分”,部分聚合结果的时候调用该方法必须返回一封装了聚合计算当前状态的对象,类似于MapReduce的combiner
- merge()接受来自terminatePartial的返回结果,进行合并,hive合并两部分聚合的时候回调用这个方法。
- terminate()终止方法返回最终函数结果。
- 案例:使用UDAF对数据进行
package week12.day01;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.hadoop.hive.serde2.io.DoubleWritable;
/**
* description: SumValueUDAF
* 定义一个UDAF自定义函数,默认要继承与UDAF类
*/
@Description(name = "SumValue",value = "Sue For Peace ",extended = "Extension: Find The Sum of col")
public class SumValueUDAF extends UDAF {
public static class SumnumDoubleUDAFEvaluator implements UDAFEvaluator {
// 在静态列内部定义一个返回值,作为当前UDAF最后的唯一返回值,因为返回值要hive调用,所以必须要使用序列化类型
private DoubleWritable result;
/**
* 初始化是把返回值设为null,避免上传调用时混淆
*/
@Override
public void init() {
result = null;
}
/**
* 定义一个函数iterate用来处理遍历多行时,每行值传进来是调用的函数
*
* @param value
* @return
*/
public boolean iterate(DoubleWritable value) {
if (value == null) {
return true;
}
if (result == null) {
result=new DoubleWritable(value.get());
} else {
result.set(result.get() + value.get());
}
return true;
}
/**
* 在map端进行执行后的结果
*
* @return
*/
public DoubleWritable terminatePartial() {
return result;
}
/**
* 接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean
*
* @param other
* @return
*/
public boolean merge(DoubleWritable other) {
return iterate(other);
}
/**
* 将最终的几个返给hive
* @return
*/
public DoubleWritable terminate() {
return result;
}
}
}
- 打包jar
- 添加jar包
add jar /week12.jar;
-- 删除jar包
delete jar /week12.jar;
-- 删除内置函数sum_UDAF
create temporary function sum_UDAF as 'week12.day01.SumValueUDAF';
drop temporary function if exists sum_UDAF;
-- 查看添加的函数sum_UDAF
desc function sum_UDAF;
select sum_UDAF(cost) --661
from day05.t_order;