最近在做案例的时候,发现有一个需要解析的字段,Hive中提供的内置函数解析不了.这时候只能自己定义Hive的内置函数了,顺便记录一下。
一、什么是UDF
UDF(User-Defined-Function) 函数其实就是一个简单的函数,执行过程就是在Hive转换成mapreduce程序后,执行java方法,类似于像Mapreduce执行过程中加入一个插件,方便扩展. UDF只能实现一进一出的操作,如果需要实现多进一出,则需要实现UDAF .
Hive可以允许用户编写自己定义的函数UDF,来在查询中使用,下面我们来定义一个UDF
二、UDF定义
1)新建项目
新建JAVA项目 并添加 hive-exec-2.1.1.jar 和hadoop-common-2.7.4.jar依赖
hive-exec-2.1.1.jar 在HIVE安装目录的lib目录下
hadoop-common-2.7.4.jar在hadoop的安装目录下的\share\hadoop\common
根据软件安装的版本不同,拉取不同的jar包
然后将jar包添加到项目依赖中
如果你创建的是Maven项目,则需要把以下依赖添加进项目Pom文件中
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.4</version>
</dependency>
2)重写evaluate
方法
新建一个类并继承UDF 并重写evaluate方法
数据类型为17-Jan
格式的,是车辆的上牌时间,其中还有好多行数据年份和月份是颠倒的,通过下列代码,我们把这个字段转成车龄
public class DataProcess extends UDF {
//申明代表月份的map
private static Map<String,String> map = new HashMap<>();
static {
//在静态代码块中给map put元素
map.put("Jan","01");
map.put("Feb","02");
map.put("Mar","03");
map.put("Apr","04");
map.put("May","05");
map.put("Jun","06");
map.put("Jul","07");
map.put("Aug","08");
map.put("Sep","09");
map.put("Oct","10");
map.put("Nov","11");
map.put("Dec","12");
}
public Text evaluate(Text text){
//数据类型 17-Jan
//切分数据 将数据切分为年份 和 月份
String[] split = text.toString().split("-");
//获取到需要的月份类型
String month=null;
String s=null;
//数据中有一些不规范的日期,年份和月份反过来了 这里我们判断一下,把他纠正
if (split[1].length()==3){
month = map.get(split[1]);
if (split[0].length() < 2) {
s = "200" + split[0] + "-" + month;
} else {
s = "20" + split[0] + "-" + month;
}
}else{
month = map.get(split[0]);
//根据月份位数的不同 来拼接成日期
if (split[1].length() < 2) {
s = "200" + split[1] + "-" + month;
} else {
s = "20" + split[1] + "-" + month;
}
}
if (month==null|| "".equals(month)){
return new Text("");
}
//将日期字符串转换为date
DateFormat df = new SimpleDateFormat("yyyy-MM");
Date parse = null;
try {
parse = df.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
//获取到当前时间 毫秒
long l = System.currentTimeMillis();
//计算时间差
long l1 = l - parse.getTime();
Long lin = 1000 * 60 * 60 * 24 * 365L;
//使用DecimalFormat 来保留两位小数
DecimalFormat decimalFormat = new DecimalFormat("0.00");//格式化小数
//获取到年份返回
String num = decimalFormat.format((float) l1 / lin);//返回的是String类型
return new Text(num) ;
}
}
3)上传并使用
编写完成后将,代码打包(带依赖)上传到集群中.
因为我是用SparkSQL操作的Hive中的数据,所以我需要重启Spark SQL 命令行,把Jar指定进去
spark-sql --jars /root/used_car/udf.jar --master spark://hbase01:7077
将自定义的方法添加进内置函数中
CREATE TEMPORARY FUNCTION huatec_date AS 'com.huatec.udf.DataProcess';
huatec_date
为内置函数的名称
com.huatec.udf.DataProcess
为jar包中方法的全类名
temporary
表示为临时方法,当会话结束后失效
这时候就可以将上牌时间解析为车龄了
一起来试试吧!!!~~~~~
SELECT
card_time,
huatec_date (card_time) AS car_age
FROM
car_used_info
LIMIT 10;
到这里一个Hive UDF 就定义好了 ~~~~~~~