内容仅截取函数部分,其他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
-
创建类,继承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; } }
-
打成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;
-
创建临时函数
CREATE TEMPORARY FUNCTION myAdd AS 'cn.wangbowen.hive.UDFDemo';
-
在查询中使用自定义函数
select myAdd(1,2); OK _c0 3
-
如果用完了,还可以删除掉这个函数
DROP TEMPORARY FUNCTION IF EXISTS myAdd;
13.4.2 GenericUDF
与Hive一起使用的通用用户定义函数(GenericUDF)。新的GenericUDF类需要从该GenericUDF类继承。GenericUDF在以下方面优于普通UDF:
- 它可以接受复杂类型的参数,并返回复杂类型。
- 它可以接受可变长度的参数。
- 它可以接受无限数量的函数签名-例如,编写接受数组的GenericUDF很容易,数组>等(任意级别的嵌套)。
- 它可以使用DeferedObject进行短路评估。
在写有关通用的UDF之前最好了解以下ObjectInspector有关的内容,可以参考另一篇文章《Hive之ObjectInspector详解》
代码
-
编写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;