UDF
-
User-Defined-Function 自定义函数 、一进一出;
-
背景
- 系统内置函数无法解决实际的业务问题,需要开发者自己编写函数实现自身的业务实现诉求。
- 应用场景非常多,面临的业务不同导致个性化实现很多,故udf很需要。
-
意义
- 函数扩展得到解决,极大丰富了可定制化的业务需求。
- IO要求-要解决的问题
- in:out=1:1,只能输入一条记录当中的数据,同时返回一条处理结果。
- 属于最常见的自定义函数,像cos,sin,substring,indexof等均是如此要求
-
实现步骤(Java创建自定义UDF类)
- 自定义一个java类
- 继承UDF类
- 重写evaluate方法
- 打包类所在项目成一个all-in-one的jar包并上传到hive所在机器
- 在hive中执行add jar操作,将jar加载到classpath中。
- 在hive中创建模板函数,使得后边可以使用该函数名称调用实际的udf函数
- hive sql中像调用系统函数一样使用udf函数
-
代码实现
- 功能要求:实现当输入字符串超过2个字符的时候,多余的字符以”…”来表示。
- 如“12”则返回“12”,如“123”返回“12…”
- 自定义类、继承UDF、重写evaluate方法已在代码中体现
import org.apache.hadoop.hive.ql.exec.UDF; /* * 功能:实现当输入字符串超过2个字符的时候,多余的字符以"..."来表示。 * 输入/输出:* 如“12”则返回“12”,如“123”返回“12..." */ public class ValueMaskUDF extends UDF{ public String evaluate(String input,int maxSaveStringLength,String replaceSign) { if(input.length()<=maxSaveStringLength){ return input; } return input.substring(0,maxSaveStringLength)+replaceSign; } public static void main(String[] args) { System.out.println(new ValueMaskUDF().evaluate("河北省",2,"..."));; } }
UDAF
-
自定义udaf函数self_count,实现系统udaf count的功能
-
Input/Output要求-要解决的问题
- in:out=n:1,即接受输入N条记录当中的数据,同时返回一条处理结果。
- 属于最常见的自定义函数,像count,sum,avg,max等均是如此要求
-
实现步骤
- 自定义一个java类
- 继承UDAF类
- 内部定义一个静态类,实现UDAFEvaluator接口
- 实现方法init,iterate,terminatePartial,merge,terminate,共5个方法. 详见下图
- 在hive中执行add jar操作,将jar加载到classpath中。
- 在hive中创建模板函数,使得后边可以使用该函数名称调用实际的udf函数
-
hive sql中像调用系统函数一样使用udaf函数
-
业务测试
输入:
输出:
- UDAF代码开发
import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.hadoop.hive.ql.exec.UDAF; import org.apache.hadoop.hive.ql.exec.UDAFEvaluator; import org.apache.log4j.Logger; /** * 实现多条数据合并成一条数据 */ // 主类继承UDAF public class StudentScoreAggUDAF extends UDAF { // 日志对象初始化 public static Logger logger = Logger.getLogger(StudentScoreAggUDAF.class); // 静态类实现UDAFEvaluator public static class Evaluator implements UDAFEvaluator { // 设置成员变量,存储每个统计范围内的总记录数 private Map<String, String> courseScoreMap; //初始化函数,map和reduce均会执行该函数,起到初始化所需要的变量的作用 public Evaluator() { init(); } // 初始化函数间传递的中间变量 public void init() { courseScoreMap = new HashMap<String, String>(); } //map阶段,返回值为boolean类型,当为true则程序继续执行,当为false则程序退出 public boolean iterate(String course, String score) { if (course == null || score == null) { return true; } courseScoreMap.put(course, score); return true; } /** * 类似于combiner,在map范围内做部分聚合,将结果传给merge函数中的形参mapOutput * 如果需要聚合,则对iterator返回的结果处理,否则直接返回iterator的结果即可 */ public Map<String, String> terminatePartial() { return courseScoreMap; } // reduce 阶段,用于逐个迭代处理map当中每个不同key对应的 terminatePartial的结果 public boolean merge(Map<String, String> mapOutput) { this.courseScoreMap.putAll(mapOutput); return true; } // 处理merge计算完成后的结果,即对merge完成后的结果做最后的业务处理 public String terminate() { return courseScoreMap.toString(); } } }
测试sql语句
select id,username,score_agg(course,score) from student_score group by id,username;
- 自定义udaf实现max:https://www.cnblogs.com/itxuexiwang/p/6263110.html
UDTF
- User-Defined Table-Generating Functions
- 要解决一行输入多行输出的问题,问题的应用场景不少
- 用udtf解决一行输入多行输出的不多,往往被lateral view explode+udf等替代实现,比直接用udtf会更简单、直接一些