用户自定义函数( UDF )是一个允许用户扩展 HiveQL 的强大的功能。正如我们将看到的,用户使用 Java 进行编码。一旦将用户自定义函数加人到用户会话中(交互式的或者通过脚本执行的),它们就将和内置 的函数一样使用,甚至可以提供联机帮助。 Hive 具有多种类型的用户自定义函数,每一种都会针对翰人数据执行特定“一类”的转换过程。
在 ETL 处理中,一个处理过程可能包含多个处理步骤。 Hive 语言具有多种方式来将上一步骤的输人通过管道传递给下一个步骤,然后在一个查询中产生众多输出。用户同样可以针对一些特定的处理过程编写自定义函数。如果没有这个功能,那么一个处理过程可能就需要包含一个 MapReduce 步骤或者需要将数据转移到另一个系统中来实现这些改变。互联系统增加了复杂性,并且增加了配置错误或其他错误的发生几率。当数据量是 GB 甚至 TB 级别时,在不同系统中转移数据,需要消耗大量的时间。与此相反, UDF 是在 Hive 查询产生的相同的恤欢进程中执行的,因此它们可以高效地执行,而且其消除了和其他系统集成时所产生的复杂度。本章涵括创建和使用 UDF 的最佳实践。
1、发现和描述函数
在编写自定义 UDF 之前,我们先来熟悉下 Hive 中自带的那些 UDF 。需要注意的是在 Hive 中通常使用“UDF“来表示任意的函数,包括用户自定义的或者内置的。 SHOW FUNCTIONS 命令可以列举出当前 Hive 会话中所加载的所有函数名称,其中包括内里的和用户加载进来的函数,加载方式稍后会进行介绍:
hive>SHOW FUNCTIONS;
abs
acos
and
array
...
函数通常都有其自身的使用文档。使用 DESCRIBE FUNCTION 命令可以展示对应函数简短的介绍:
hive > DBSCRIBE FUNCTION concat;
函数也可能包含更多的详细文档,可以通过增加 EXTENDED 关键字进行查看:
2、调用函数
如果想使用函数,只摇要在查询中通过调用函数名,并传人器要的参数即可。某些函数需要指定特定的参数个数和参数类型,而其他函数可以传人一组参数,参数类型可以是多样的。和关键字一样,函数名也是保留的字符串:
select concat(column1,column2) AS x from table;
3、标准函数
用户自定义函数(英文缩写为 UDF )这个术语在狭义的概念上还表示以一行数据中的一列或多列数据作为参数然后返回结果是一个值的函数。大多数函数都是属于这类的。
我们使用的例子中包含了很多的数学函数。例如 round()和floor(),其可以将 DOUBLE 类型转换为 BIGINT 类型.还有abs(),这个函数可以返回数值的绝对值。
需要注意的是,这些 UDF 同样可以返回一个复杂的对象,例如 arry 、 map或者 struct 。
4、聚合函数
另一种函数是聚合函数。所有的聚合函数、用户自定义函数和内置函数,都统称为用户自定义聚合函数( UDAF )。
聚合函数接受从零行到多行的零个到多个列,然后返回单一值。这样的例子包括数学函数 sum(),其返回所有输人求和后的值. avg()函数会计算所有输入的值的平均值. min()和 max()函数,可以分别返回输人值中的最小值和最大值:
聚合方法通常和 GROUP BY 语句组合使用。
5、表生成函数
Hive 所支持的第 3 类函数就是表生成函数。和其他函数类别一样,所有的表生成函数,包括用户自定义的和内置的,都统称为用户自定义表生成函数( UDTF )。
表生成函数接受零个或多个辅人,然后产生多列或多行输出。例如, array 函数就是将一列输人转换成一个数组输出的。下面的查询语句中使用了 array 函数:
hive > SELECT array(1,2,3) FROM dual ;
[1 , 2, 3]
explode()函数以 array 类型数据作为输人,然后对数组中的数据进行迭代,返回多行结果,一行一个数组元素值。
hive > SELECT explode(array(1,2,3)) AS element FROM src ;
1
2
3
不过, Hive 只允许表生成函数以特定的方式使用。例如,一个显著的限制就是,我们无法从表中产生其他的列。例如这个查询可能是前面我们对于 employees 表进行的操作。我们可能需要列举出每名雇员的下属员工:
不过, Hive 提供了一个 LATERAL VIEW 功能来实现这种查询:
需要注意的是,对于职位不是经理的雇员(即没有下属员工的人)来说是没有输出行的,例如 Bill King 和Toddd Jones 就不会有对应的输出行。因此,输出中将会产生零到多行新纪录。通过 LATERAL VIEW 可以方便地将 explode 这个 UDTF 得到的行转列的结果集合在一起提供服务。使用 LATERAL VlEW 需要指定视图别名和生成的新列的别名,对于本例,其分别是 subview 和 sub。
6、编写UDF
下面我们开始编写自己的 UDF 。
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public final class Lower extends UDF{
public Text evaluate(final Text s){
if(s==null){return null;}
return new Text(s.toString().toLowerCase());
}
}
编写一个 UDF ,需要继承 UDF 类并实现 evaluate()函数。在查询执行过程中,查询中对应的每个应用到这个函数的地方都会对这个类进行实例化。对于每行输入都会调用到 evaluate()函数。而 evaluate()处理后的值会返回给 Hive 。同时用户是可以重载 evaluate 方法的。 Hive会像 Java 的方法重载一样,自动选择匹配的方法。
提示:UDF 中evaluate() 函数的参数和返回位类型只能是 Hive 可以序列化的数据类型.例如,如果用户处理的全是数值,那么 UDF 的愉出参数类型可以是基本数据类型 int 、 Integer 封装的对象或者是一个 IntWritable 对象,也就是 Hadoop 对整型封装后的对象.用户不雷要特别地关心将调用到哪个类型,因为当类型不一致的时候, Hive 会自动将类型转换成匹配的类型.禽要记住的是, null 在 Hive 中对于任何数据类型都是合法的,但是对于 Java 基本数据类型,不能是对象,也不能是 null。如果想在 Hive 中使用 UDF .那么而要将 Java 代码进行编译,然后将编译后的 UDF 二进制类文件打包成一个 JAR 文件。然后,在 Hive 会话中,将这个 JAR 文件加人到类路径下,再通过 CREATE FUNCTION 语句定义好使用这个 Java 类的函数:
hive > ADD JAR /full/path/udf.jar;
hive > CREATE TEMPORARY FUNCTION zodac AS 'org.apache.hadoop.hive.contrib.udf.example.UDF';
需要注意的是, JAR 文件路径是不需要用引号括起来的,同时,到目前为止这个路径需要是当前文件系统的全路径。 Hive 不仅仅将这个 JAR 文件加人到 classPath 下,同时还将其加人到了分布式缓存中,这样整个集群的机器都是可以获得该 JAR 文件的。现在这个判断星座的 UDF 可以像其他的函数一样使用了。需要注意下 CREATE FUNCTION 语句中的 TEMPoRARY 这个关键字。当前会话中声明的函数只会在当前会话中有效。因此用户需要在每个会话中都增加 JAR 然后创建函数。不过,如果用户需要频繁地使用同一个 JAR 文件和函数的话,那么可以将相关语句增加到$HOME/.hiverc文件中去。
再次说明下, UDF 允许用户在 Hive 语言中执行自定义的转换过程。通过上面那个 UDF , Hive 现在可以通过用户生日计算得到相应的星座名称了,当然也可以做其他的聚合和转换过程。当我们使用完自定义 UDF 后,我们可以通过如下命令删除此函数:
hive > DROP TEMPORARY FUNCTION IF EXISTS zodiac;
像通常一样, IF EXISTS 是可选的。如果增加此关键字,则即使函数不存在也不会报错。