hive从入门到实战No.3

本文详细介绍了如何在Hive中自定义UDAF(User Defined Aggregation Function)函数,包括创建自定义计数函数self_count和多条学生成绩合并的UDAF。通过实现UDAF的init、iterate、terminatePartial、merge和terminate方法,实现了类似系统count函数的功能。此外,还展示了如何部署和测试自定义UDAF,并讨论了UDTF(User-Defined Table-Generating Functions)在行转列场景的应用,以及如何使用lateral view explode和UDF替代UDTF实现特定业务需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

UDAF(user defined aggregation function)

  • 自定义udaf函数self_count,实现系统udaf count的功能
  1. in:out=n:1,即输入N条数据,返回一条处理结果,即列转行。
  2. 最常见的系统聚合函数,如count,sum,avg,max等
  • 实现步骤
  1. 自定义一个java类
  2. 继承UDAF类
  3. 内部定义一个静态类,实现UDAFEvaluator接口
  4. 实现方法init,iterate,terminatePartial,merge,terminate共5个方法.

 

  • hive中执行add jar操作,将jar加载到classpath中。
  • hive中创建模板函数,使得后边可以使用该函数名称调用实际的udf函数
  • hive sql中像调用系统函数一样使用udaf函数
  • 代码实现

实现与hive原生的count相似的计数功能。

如:select count(1) from tablename 或者select key,count(1) from tablename group by key;

import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.log4j.Logger;
/**
* 自行实现sql的count操作
*/
//主类继承UDAF
public class DIYCountUDAF extends UDAF {  
    //日志对象初始化,使访类有输出日志的能力
    public static Logger logger=Logger.getLogger(DIYCountUDAF.class);
    
    //静态类实现UDAFEvaluator
    public static class Evaluator implements UDAFEvaluator {  
        //设置成员变量,存储每个统计范围内的总记录数
        private int totalRecords;  
        //初始化函数,map和reduce均会执行该函数,起到初始化所需要的变量的作用
        public Evaluator() {  
            init();  
        }  
        //初始化,初始值为0,并日志记录下相应输出
        public void init() {  
            totalRecords = 0;  
            logger.info("init totalRecords="+totalRecords);
        }  
        //map阶段,返回值为boolean类型,当为true则程序继续执行,当为false则程序退出  
        public boolean iterate(String input) {
            //当input输入不为空的时候,即为有值存在,即为存在1行,故做+1操作
            if (input != null) {  
                totalRecords += 1;  
            }  
            //输出当前组处理到第多少条数据了
            logger.info("iterate totalRecords="+totalRecords);
            return true;  
        }  
        /**
         * 类似于combiner,在map范围内做部分聚合,将结果传给merge函数中的形参mapOutput  
         * 如果需要聚合,则对iterator返回的结果处理,否则直接返回iterator的结果即可
         */
        public int terminatePartial() {  
            logger.info("terminatePartial totalRecords="+totalRecords);
            return totalRecords;  
        }
        
        // reduce 阶段,用于逐个迭代处理map当中每个不同key对应的 terminatePartial的结果
        public boolean merge(int mapOutput) {  
            totalRecords +=mapOutput;  
            logger.info("merge totalRecords="+totalRecords);
            return true;  
        }  
        //处理merge计算完成后的结果,此时的count在merge完成时候,结果已经得出,无需再进一次对整体结果做处理,故直接返回即可
        public int terminate() {  
            logger.info("terminate totalRecords="+totalRecords);
            return totalRecords;  
        }  
    }  
}
  • 布署步骤

跟udf完全一致

  • 加载jar包、声明函数、使用函数

跟udf完全一致

  • 测试运行

与count一样,使用前边定义的临时udaf函数。

  • 案例2

自定义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();
        }
    }
}

 布署过程与之前相同

测试脚本

select 
id,username,score_agg(course,score) 
from 
student_score 
group by id,username;

 UDTF(User-Defined Table-Generating Functions

  • 解决一行输入多行输出,即1:n,即行转列应用
  • 往往被lateral view explode+udf等替代实现,比直接用udtf会更简单、直接、更灵活一些
  • 本节由学员自学实现,如何用lateral view explode+udf替代udtf实现
  • lateral view explode+udf替代udtf应用案例
  1. 需求

将一个array类型按列存储的学生成绩表,转变成按行来显示,学生名字超过2个字符的,后边用"..."来代替。

数据准备

学生成绩表

 通过lateral view explode实现行转列

select id,name,score 
from test_array 
lateral view 
explode(score_array) score_table as score;
  1. 加入udf处理业务需求
select id,mask(name,2,'...'),score 
from test_array 
lateral view 
explode(score_array) score_table as score;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值