Hive自定义UDF、UDAF、UDTF函数实例与区别

Hive中有三种自定义函数

在这里插入图片描述
分类
1、用户定义函数(user-defined function)UDF;
2、用户定义聚集函数(user-defined aggregate function,UDAF);
3、用户定义表生成函数(user-defined table-generating function,UDTF)。
介绍
UDF操作作用于单个数据行,并且产生一个数据行作为输出。大多数函数都属于这一类(比如数学函数和字符串函数)。
UDAF 接受多个输入数据行,并产生一个输出数据行。像COUNT和MAX这样的函数就是聚集函数。
UDTF 操作作用于单个数据行,并且产生多个数据行-------一个表作为输出。lateral view explore()
简单来说:
UDF:返回对应值,一对一
UDAF:返回聚类值,多对一
UDTF:返回拆分值,一对多

一、定义UDF具体实现

  1. 创建一个maven工程

maven工程的创建不多做赘述,网上很多

  1. 添加pom文件的依赖

添加依赖主要是添加hive-exec这个jar包的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.hopson</groupId>
    <artifactId>hive-udf-weekofyear</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--添加CDH的仓库-->
    <repositories>
        <repository>
            <id>cloudera</id>
            <url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
        </repository>
    </repositories>

    <properties>
        <projcet.build.sourceEncoding>UTF-8</projcet.build.sourceEncoding>
        <hadoop.version>3.0.0-cdh6.2.1</hadoop.version>
        <hive.version>2.1.1-cdh6.2.1</hive.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>${hive.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>
</project>
  1. UDF函数编写

UDF函数自定义主要是继承hive-exec中UDF这个类,重写evaluate方法实现UDF自定义。

package org.hopson;
import org.junit.Test;
import org.apache.hadoop.hive.ql.exec.UDF;
public class helloUdf extends UDF {
    public String evaluate (String input){
        return "Hello:"+input;
    }
    //本地测试
    @Test
    public void Test() throws ParseException {
        String evaluate = evaluate("2018-12-31");
        System.out.println(evaluate);
    }
}

4.导入hive 自定义jar包
继承UDF借口
实现evaluate方法
5.临时函数的使用
进入hive的交互shell中

1. 创建存放jar的目录
[hdfs@dp-hadoop-3 jar]#   rz
2. 上传自定义udf的jar
hive> add jar /home/hadoop/jar/hive-udf-weekofyear-1.0-SNAPSHOT.jar
3. 创建临时函数
hive> create temporary function sayhelloUDF as 'org.hopson.helloUdf ';
4. 验证
hive> select sayhelloUDF ("Hello World!");

6.永久函数的使用:

1. 创建存放jar的目录
   hdfs dfs -mkdir -p /user/hiveUDF;
2. 把自定义函数的jar上传到hdfs中.
    hdfs dfs -put hive-udf-weekofyear-1.0-SNAPSHOT.jar  ' /user/hiveUDF/';
7. 创建永久函数
hive> create function sayhelloUDF as 'org.hopson.helloUdf' using jar 'hdfs:///path/user/hiveUDF/hive-udf-weekofyear-1.0-SNAPSHOT.jar';
8. 验证
hive> select sayhelloUDF  ("Hello World");
hive> show functions;

二、定义UDAF具体实现

1、实现UFAF的步骤
引入如下两下类

import org.apache.hadoop.hive.ql.exec.UDAF  
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator

函数类需要继承UDAF类,计算类Evaluator实现UDAFEvaluator接口
Evaluator需要实现UDAFEvaluator的init、iterate、terminatePartial、merge、terminate这几个函数。
a)init函数实现接口UDAFEvaluator的init函数。
b)iterate接收传入的参数,并进行内部的迭代。其返回类型为boolean。
c)terminatePartial无参数,其为iterate函数遍历结束后,返回遍历得到的数据,terminatePartial类似于 hadoop的Combiner。
d)merge接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean。
e)terminate返回最终的聚集函数结果。
2、实例:

package org.hopson;

import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
public class Avg extends UDAF {
    public static class AvgState {
        private long mCount;
        private double mSum;
    }
    public static class AvgEvaluator implements UDAFEvaluator {
        AvgState state;
        public AvgEvaluator() {
            super();
            state = new AvgState();
            init();
        }
        /**
         * init函数类似于构造函数,用于UDAF的初始化
         */
        public void init() {
            state.mSum = 0;
            state.mCount = 0;
        }
        /**
         * iterate接收传入的参数,并进行内部的轮转。其返回类型为boolean
         * @param
         * @return
         */
        public boolean iterate(Double o) {
            if (o != null) {
                state.mSum += o;
                state.mCount++;
            }
            return true;
        }
        /**
         * terminatePartial无参数,其为iterate函数遍历结束后,返回轮转数据, * terminatePartial类似于hadoop的Combiner
         * @return
         */
        public AvgState terminatePartial() {
            // combiner
            return state.mCount == 0 ? null : state;
        }
        /**
         * merge接收terminatePartial的返回结果,进行数据merge操作,
         * @param
         * @return其返回类型为boolean
         */
        public boolean merge(AvgState avgState) {
            if (avgState != null) {
                state.mCount += avgState.mCount;
                state.mSum += avgState.mSum;
            }
            return true;
        }
        /**
         * terminate返回最终的聚集函数结果
         *  @return
         */
        public Double terminate() {
            return state.mCount == 0 ? null : Double.valueOf(state.mSum / state.mCount);
        }
    }
}

3、Hive中使用UDAF
将java文件编译成hive-udaf-avg.jar

进入hive客户端添加jar包
hive>add jar /home/hadoop/hive-udaf-avg.jar
创建临时函数
hive>create temporary function udaf_avg 'org.hopson.Avg'
查询语句
hive>select udaf_avg(people.age) from people
销毁临时函数
hive>drop temporary function udaf_avg

4、总结
通过上面的介绍,可以看到UDAF的用法与UDF的区别了,UDF虽然可以接收多个入参,但是参数个数是固定的(其实也可以不固定,只要evaluate方法的参数类型是变长参数即可,但是一般不这么用),而UDAF的入参是元素个数不固定的集合,这个集合只要可遍历(使用Evaluator的iterate方法遍历)即可,上面的入参是people表的所有age列。

UDF是对只有单条记录的列进行的计算操作,而UDFA则是用户自定义的聚类函数,是基于表的所有记录进行的计算操作。

三、定义UDTF具体实现

UDTF(User-Defined Table-Generating Functions)用来解决输入一行输出多行(one-to-many
maping)的需求。

1.继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF。
2.实现initialize(),process(),close()三个方法。
3.UDTF首先会调用initialize()方法,此方法返回UDTF的返回行的信息(返回个数,类型)。
4.初始化完成后会调用process()方法,对传入的参数进行处理,可以通过forward()方法把结果返回。
5.最后调用close()对需要清理的方法进行清理。

示例:
使用UDTF对"Key:Value"这种字符串进行切分,返回结果为Key,Value两个字段。

package org.hopson;

import java.util.ArrayList;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
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;

public class UDTFExplode extends GenericUDTF{

    @Override
    public void close() throws HiveException {
        // TODO Auto-generated method stub
    }
    @Override
    public StructObjectInspector initialize(ObjectInspector[] args)
            throws UDFArgumentException {
        if (args.length != 1) {
            throw new UDFArgumentLengthException("ExplodeMap takes only one argument");
        }
        if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {
            throw new UDFArgumentException("ExplodeMap takes string as a parameter");
        }
        ArrayList<String> fieldNames = new ArrayList<String>();
        ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>();
        fieldNames.add("col1");
        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        fieldNames.add("col2");
        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames,fieldOIs);
    }
    @Override
    public void process(Object[] args) throws HiveException {
        String input = args[0].toString();
        String[] test = input.split(";");
        for(int i=0; i<test.length; i++) {
            try {
                String[] result = test[i].split(":");
                forward(result);
            } catch (Exception e) {
                continue;
            }
        }
    }
}

1.打包发送到服务器。

2.添加到Hive环境中:

hive (hive)> add jar /usr/local/src/hive-udtf-explode.jar
Added /usr/local/src/udtf.jar to class path
Added resource: /usr/local/src/udtf.jar

3.创建临时函数:

hive (hive)> create temporary function explode_map as 'org.hopson.org.hopson';
OK
Time taken: 0.0080 seconds

4.查询(UDTF有两种使用方式,一种是直接放到select后面,另外一种是和lateral view一起使用):

hive (hive)> select explode_map('name:lavimer;age:23') as (col1,col2) from employees;
//MapReduce
OK
col1	col2
name	lavimer
age	23
name	lavimer
age	23
name	lavimer
age	23

注:不可以添加其他字段使用,如下:

select a, explode_map(properties) as (col1,col2) from src
不可以嵌套调用:

select explode_map(explode_map(properties)) from src
不可以和group by/cluster by/distribute by/sort by一起使用:

select explode_map(properties) as (col1,col2) from src group by col1, col2
可以和lateral view一起使用:

hive (hive)> select user.id,employees.col1,employees.col2 from user lateral view explode_map('name:lavimer,age:23') employees as col1,col2;
//MapReduce...
OK
id	col1	col2
1	name	lavimer
1	age	23
2	name	lavimer
2	age	23
3	name	lavimer
3	age	23

注:此方法更为方便使用。执行过程相当于单独执行了两次抽取,然后union到一个表里。

posted @   lx991818  阅读(659)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示