Hive自定义函数
为啥会有自定义函数
因为hive自带函数不能满足开发的需求,所以需要我们自己写hive函数,来实现一些复杂的转换或者其自身不能完成的方法。
UDF(一对一)
将输入的一个值,根据一定的转换,生成另一个格式的值。在这里UDF函数需要继承UDF类,重写evaluate(),而里面的参数根据需求传入。
public class MyUDF extends UDF {
public String evaluate(int val){
if(val==0){
return "null";
}else {
return String.valueOf(val);
}
}
}
加载函数方式:
hive (default)> add jar /package/ad_release-1.0-SNAPSHOT.jar;
hive (default)> create temporary function myudtf as "com.az.udf.MyUDTF";
UDTF(一对多)
根据输入的数据,按照一定的拆分方式,将一行变多行,或者将一行变多行多列。里面有三个组件,initial、process、close三个组件,initial是返回输出的列名和类型,process是对传入的数据进行分析,传入的数据被装在一个Object的数组,得到里面每个数据然后将多行数据传给outList集合,再通过forward将数据上传。close模块则是针对在函数中会使用到一些流,比如字符流、字节流等的操作,将这些珍稀资源进行一个释放,避免资源浪费情况。
public class MyUDTF extends GenericUDTF {
List<String> outList = new ArrayList<>();
@Override
public void process(Object[] objects) throws HiveException {
//以0或1开始
int start_with = Integer.parseInt(objects[0].toString());
//传入日期
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = simpleDateFormat.parse(objects[1].toString());
} catch (ParseException e) {
e.printStackTrace();
}
//确定步长
long length = 0;
try {
length = (date.getTime()-simpleDateFormat.parse("1990-12-19").getTime())/(1000*60*60*24);
} catch (ParseException e) {
e.printStackTrace();
}
if(date!=null){
Calendar can = Calendar.getInstance();
for(int i = start_with;i<length;i++){
can.setTime(date);
can.add(Calendar.DAY_OF_MONTH,i);
System.out.println(simpleDateFormat.format(can.getTime()));
}
}
}
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//1.定义输出数据列名和类型
List<String> col1 = new ArrayList<>();
List<ObjectInspector> col1OIs = new ArrayList<>();
//2.添加输出数据的列明和类型
col1.add("col1");
col1OIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(col1,col1OIs);
}
@Override
public void close() throws HiveException {
}
}
UDAF(多对一)
比如hive的自带函数sum()和count()等聚合类函数,这个是将一张表中的多行数据进行一个汇总聚合,通过设置全局的集合来进行一个迭代的操作,操作完成后再将返回结果送回。这种函数多与group by联合使用。
public class MyUDAF extends UDAF {
public static Logger logger = Logger.getLogger(MyUDAF.class);
public static class Evaluator implements UDAFEvaluator{
//定义集合来存储中间数据
private Map<String,String> source ;
public Evaluator(){
//初始化
init();
}
@Override
public void init() {
source = new HashMap<String,String>();
}
public boolean iterate(String course,String score){
if(course == null||score==null){
return true;
}
source.put(course,score);
return true;
}
public Map<String,String> terminatePartial(){
return source;
}
public boolean merge(Map<String,String> mapOut){
this.source.putAll(mapOut);
return true;
}
public String terminate(){
return source.toString();
}
}
}
输入数据:
sc.sid sc.cid sc.score
1 2 99
3 4 88
1 3 55
2 1 77
sid _c1
1 {2=99, 3=55}
2 {1=77}
3 {4=88}
public class MyUDAF extends UDAF {
public static Logger logger = Logger.getLogger(MyUDAF.class);
public static class Evaluator implements UDAFEvaluator{
//定义一个List存储传入的数值
private List<String> source ;
//进行初始化
public Evaluator(){
init();
}
@Override
public void init() {
source = new ArrayList<String>();
}
//将每行数据值传入,如果值是空则返回,如果不为空,将其加在List里面
public boolean iterate(String score){
if(score==null){
return true;
}
source.add(score);
return true;
}
public List<String> terminatePartial(){
return source;
}
//将map的list再进行合并
public boolean merge(List<String> listOut){
this.source.addAll(listOut);
return true;
}
//将完整的数据进行操作聚合
public int terminate(){
int sum = 0;
for(String score:source){
sum+=Integer.parseInt(score);
}
return sum;
}
}
}
输入数据:
sc.sid sc.cid sc.score
1 2 99
3 4 88
1 3 55
2 1 77
sid _c1
1 144
2 77
3 88