Hive之自定义函数(UDF、UDAF、UDTF)

内容仅截取函数部分,其他Hive学习笔记见个人博客 https://wangbowen.cn/2020/08/27/Hive%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

十三、函数

13.1 发现和描述函数

-- 可以看到hive当前会话的所有函数名称,包括内置和用户自定义的
show functions;
-- 查看函数的简介(加上extended显示更详细的文档)
describe function [extended] <函数名>;

13.2 调用函数

函数名(参数列表)

13.3 函数的类别

  • UDF:用户自定义函数

  • UDAF:用户自定义聚合函数

  • UDTF:用户自定义表生成函数。接收零个或多个输入,然后长生多列或多汗输出。如 array函数 和 explode 函数。不过,hive只允许表生成函数以特定的方式使用。比如:无法从表中产生其他的列。

    select name, exploed(subordinates) from employees;  -- 这个是错误的!!!!
    

    不过,hive 提供了一个 LATERAL VIEW 功能来实现这种查询:

    select name, sub
    from employees
    LATERAL VIEW exploed(subordinates) subView AS sub;
    

    通过LATERAL VIEW 可以方便地将exploed这个UDTF得到的行转列的结果集合在一起提供服务。使用LATERAL VIEW 需要指定视图别名和生成的新列的别名,本例来说分别是 subView 和 sub。

13.4 用户自定义函数

导入pom依赖

<dependency>
    <groupId>org.apache.hive</groupId>
    <artifactId>hive-exec</artifactId>
    <version>3.1.2</version>
</dependency>
13.4.1 UDF
  1. 创建类,继承UDF,添加注解,实现evaluate函数

    package cn.wangbowen.hive;
    
    import org.apache.hadoop.hive.ql.exec.Description;
    import org.apache.hadoop.hive.ql.exec.UDF;
    
    /**
     * UDFDemo class
     * 用户自定义函数
     */
    
    //@Description 这是描述。用于 describe function [extended] <函数名>;
    @Description(name = "myAdd",
                value = "myAdd(int a, int b ==> return a + b)",
                extended = "Example:\n" +
                        " > myAdd(1, 1) ==> 2" +
                        " > myAdd(1, 1, 1) ==> 3")
    // 需要继承 UDF
    public class UDFDemo extends UDF {
         
    
        // 实现 evaluate()函数
        // 注意,参数和返回值类型只能是hive可以序列化的数据类型。如数值,那么可以用int或Integer
        public int evaluate(int a ,int b) {
         
            return a + b ;
        }
    
        // 可以重载,调用的时候会自动匹配
        public int evaluate(int a ,int b , int c) {
         
            return a + b + c;
        }
    }
    
    
  2. 打成jar包,把jar包添加到hive的类路径下重进入hive,

    cp Hive-1.0-SNAPSHOT.jar /home/spark/app/hive-1.1.0-cdh5.15.1/lib/
    

    或者使用命令添加全路径

    ADD JAR /home/spark/app/hive-1.1.0-cdh5.15.1/lib/Hive-1.0-SNAPSHOT.jar;
    
  3. 创建临时函数

    CREATE TEMPORARY FUNCTION myAdd AS 'cn.wangbowen.hive.UDFDemo';
    
  4. 在查询中使用自定义函数

    select myAdd(1,2);
    OK
    _c0
    3
    
  5. 如果用完了,还可以删除掉这个函数

    DROP TEMPORARY FUNCTION IF EXISTS myAdd;
    
13.4.2 GenericUDF

与Hive一起使用的通用用户定义函数(GenericUDF)。新的GenericUDF类需要从该GenericUDF类继承。GenericUDF在以下方面优于普通UDF:

  1. 它可以接受复杂类型的参数,并返回复杂类型。
  2. 它可以接受可变长度的参数。
  3. 它可以接受无限数量的函数签名-例如,编写接受数组的GenericUDF很容易,数组>等(任意级别的嵌套)。
  4. 它可以使用DeferedObject进行短路评估。

在写有关通用的UDF之前最好了解以下ObjectInspector有关的内容,可以参考另一篇文章《Hive之ObjectInspector详解》

代码

  1. 编写JAVA类,然后打包

    package cn.wangbowen.hive;
    
    import org.apache.hadoop.hive.ql.exec.Description;
    import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
    import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
    import org.apache.hadoop.hive.ql.metadata.HiveException;
    import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
    import org.apache.hadoop.hive.ql.udf.generic.GenericUDFUtils;
    import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
    
    /**
     * GenericUDFNvl class
     * GenericUDF 是更为复杂的抽象概念,但其支持更好的null值处理,同时可以处理一些标准UDF无法支持的编程操作
     */
    
    // 描述
    @Description(name = "nvl",
            value = "nvl(value, default_value) - 如果value非空返回value,否则返回default_value",
            extended = "Example:\n" +
                    " > SELECT nvl(null, 'bla') FROM src LIMIT 1;\n")
    // 继承 GenericUDF
    public class GenericUDFNvl extends GenericUDF {
         
        // returnOIResolver 是一个内置的类,其通过获取非null值的变量的类型并使用这个数据类型来确定返回值类型
        private GenericUDFUtils.ReturnObjectInspectorResolver returnOIResolver;
    
        private ObjectInspector[] argumentOIs;
    
    
        /**
         * initialize方法会被输入的每个参数调用,并最终传入到一个ObjectInspector对象中。
         * 这个方法的目标是确定函数的返回类型。(重点)
         * 如果传入的方法的类型是不合法的,这时用户同样可以向控制台抛出一个Exception。
         */
        @Override
        public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {
         
            // 将传入的参数复制到私有变量
            argumentOIs = objectInspectors;
            // 检查输入参数合法性:如果参数个数不是2,那么抛出异常
            if (objectInspectors.length != 2) {
         
                throw new UDFArgumentException("The operator 'NVL' accepts 2 arguments.");
            }
            returnOIResolver = new GenericUDFUtils.ReturnObjectInspectorResolver(true);
            if (!(returnOIResolver.update(objectInspectors[0]) && returnOIResolver.update(objectInspectors[1]))) {
         
                throw new UDFArgumentTypeException(2, "The 1st and 2nd args of function NLV should have the same type," +
                        "but they are different:\"" + objectInspectors[0].getTypeName() + "\" and \"" +
                        objectInspectors[1].getTypeName() + "\"");
            }
            return returnOIResolver.get();
        }
    
        /**
         * evaluate方法的输入是一个DeferredObject对象数组,而initialize方法中创建的returnOIResolver对象就用于从DeferredObject对象中
         * 获取到值。在这种情况下,这个函数将会返回第一个非null的值
         */
        @Override
        public Object evaluate(DeferredObject[] deferredObjects) throws HiveException {
         
            Object retVal = returnOIResolver.convertIfNecessary(deferredObjects[0].get(), argumentOIs[0]);
            // 如果第一个值为空,返回第二个
            if (retVal == null) {
         
                retVal = returnOIResolver.convertIfNecessary(deferredObjects[1].get(), argumentOIs[1]);
            }
            return retVal;
        
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值