Hive自定义函数--UDAF

hive的UDAF函数做的是多进一出的聚合操作,它的写法和其他的两种函数有些不同,分为两部分,一部分叫做函数类,一部分叫做计算类,计算类在函数类内部,下面我给大家提供一个做计数操作的UDAF例子,希望可以帮到大家

但是我有一句前言告诉大家,Hive的UDAF函数可以说是已经无法使用了,因为它的编写在正式开发的时候不是很方便,所以随着spark的兴起,大家更愿意使用spark去处理数据,同时spark也提供了非常强大的功能,且编写更加便捷,所以对于Hive的UDAF函数,大家知道有这样一个东西就可以,不要去试着开发了,因为我自己当初搞的时候发现Hive的UDAF虽然API没有被删除,但是早已被列为不推荐使用行列,且所依赖的包可能拉取不到,就算你搞到包了,编译的时候也会发现,因为API的过时,所以无法编译成功

pom如下:

<?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>com.wy</groupId>
    <artifactId>func</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec -->
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>1.2.1</version>
        </dependency>
    </dependencies>

</project>

代码如下

package com.wy;

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

/**
 * 函数类继承UDAF,其实函数类就相当于整个函数的一个包装,或者说是个盒子,它用来装下这个函数所有的东西
 */
public class MyUDAF extends UDAF {

    /*
    在函数类中准备自定义函数需要的东西
    当前例子是用来做计数的因此准备一个计数用变量

    这里可能会有人疑问:只是计数为什么不直接用一个值变量,而是要写一个类
    别着急往下看
     */
    public static class MyCount{
        //最好不要直接写int count=0
        int count;
    }

    /**
     * 下面准备的是计算类
     * 计算类实现UDAFEvaluator接口
     * 重写init、iterate、terminatePartial、merge、terminate
     * 但是说是重写,但是由于这个API已经过时了,导致需要的方法出不来,大家手敲一下就好
     */
    public static class MyEvaluator implements UDAFEvaluator {
    
        //这里准备计算所需要的资源,为了节省资源最好使用public static修饰一下
        public static MyCount count;

        public MyEvaluator() {
            /*
            函数运行时计算类会被实例化
            我们要在计算类的构造器中初始化
            但是注意,这里初始化的不是我们一般理解的一步到位的赋具体值
            而是用只用来初始化资源,如实例化对象等预热操作
            而具体的赋值操作在init中执行,这就是为什么我前面写了一个类
            就是为了在这里让大家知道计算类的构造器和init方法到底初始化的是什么
             */
            count=new MyCount();
            /*
            这里调用一下init方法,因为我们将执行函数内部更加详细的初始化操作的任务交给了它

            这里说的内部我给大家说一个例子大家就明白了:
            如果你运行的是group之后的计数操作,那么init方法就是在
            一组数据计数之后做计数归零的那个角色,而外面的那个构造器,只是在构造这个函数的时候起效

            怎么样看到这里是不是突然想来一句 搜嘎斯内

             */
            init();
        }

        /**
         * 这里进行初始化
         */
        public void init() {
            count.count=0;
        }

        /**
         * iterate接收传入的参数,被函数内部多次调用,它的地位和MapReducer的map阶段等价
         * 我们的聚合计算操作就在这里实现
         * 但是它的返回值类型是个布尔
         * 作用是对所要执行的业务做成功与否判断
         * 大家写的时候根据当下的业务需要书写
         * 接收的参数类型也是按照业务需要来
         * 本例不是很需要具体值所有这里随便写了一个Double型
         * 但是我要说一句这个方法体正常的逻辑来说,应该是聚合成功返回一个成功也就是true的返回值,但是由于这个API过时了,没有办法正常运行,所以我不确定为false的时候效果是什么
         */
        public boolean iterate(Double o) {
            if (o != null) {
                count.count++;
            }
            return true;
        }

        /**
         * terminatePartial无参数
         * 它做的工作是用来操作iterate的结果的
         * 类似于hadoop的小文件合并
         * 过滤不必要数据的操作
         * 如果没有需要那么直接返回就需要值好
         * 但是工作中为了团队合作的我们还是会书写该方法
         * 在这里简单的做一个结果验证操作
         * 因为MR的map不止有一个同理iterate也可能不止一个
         * 他们的结果我们要过滤一下
         */
        public MyCount terminatePartial() {
            return count.count == 0 ? null : count;
        }

        /**
         * merge处理terminatePartial的结果做合并结果操作
         * 与前面的一样,我也没有成功试出为FALSE的效果是什么
         */
        public boolean merge(MyCount c) {
            if (c != null) {
                count.count+=c.count;
            }
            return true;
        }

        /**
         * terminate处理并返回最终的聚集函数结果
         */
        public int terminate() {
            return count.count == 0 ? null : count.count;
        }
    }
}

使用SparkSQL和Hive API,可以通过以下步骤实现用户自定义函数(UDF)、聚合函数UDAF)和表生成函数(UDTF): 1. 编写自定义函数的代码,例如: ``` // UDF def myUDF(str: String): Int = { str.length } // UDAF class MyUDAF extends UserDefinedAggregateFunction { override def inputSchema: StructType = StructType(StructField("value", StringType) :: Nil) override def bufferSchema: StructType = StructType(StructField("count", IntegerType) :: Nil) override def dataType: DataType = IntegerType override def deterministic: Boolean = true override def initialize(buffer: MutableAggregationBuffer): Unit = { buffer(0) = 0 } override def update(buffer: MutableAggregationBuffer, input: Row): Unit = { buffer(0) = buffer.getInt(0) + input.getString(0).length } override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = { buffer1(0) = buffer1.getInt(0) + buffer2.getInt(0) } override def evaluate(buffer: Row): Any = { buffer.getInt(0) } } // UDTF class MyUDTF extends GenericUDTF { override def initialize(args: Array[ConstantObjectInspector]): StructObjectInspector = { // 初始化代码 } override def process(args: Array[DeferedObject]): Unit = { // 处理代码 } override def close(): Unit = { // 关闭代码 } } ``` 2. 将自定义函数注册到SparkSQL或Hive中,例如: ``` // SparkSQL中注册UDF spark.udf.register("myUDF", myUDF _) // Hive中注册UDF hiveContext.sql("CREATE TEMPORARY FUNCTION myUDF AS 'com.example.MyUDF'") // Hive中注册UDAF hiveContext.sql("CREATE TEMPORARY FUNCTION myUDAF AS 'com.example.MyUDAF'") // Hive中注册UDTF hiveContext.sql("CREATE TEMPORARY FUNCTION myUDTF AS 'com.example.MyUDTF'") ``` 3. 在SQL语句中使用自定义函数,例如: ``` -- 使用SparkSQL中的UDF SELECT myUDF(name) FROM users -- 使用Hive中的UDF SELECT myUDF(name) FROM users -- 使用Hive中的UDAF SELECT myUDAF(name) FROM users GROUP BY age -- 使用Hive中的UDTF SELECT explode(myUDTF(name)) FROM users ``` 以上就是使用SparkSQL和Hive API实现用户自定义函数(UDF、UDAF、UDTF)的步骤。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值