真实 Hive 面试题:统计每月有效会员数量(完整方案 + 代码详解)
·
这是一道大数据 Hive 面试题,核心考察:自定义 UDTF 函数开发、日期处理、行列转换、会员有效周期统计。我把题目完整拆解、代码逐行注释、执行流程讲透,面试 / 工作直接复用。
一、业务需求
给定会员表:会员ID、会员生效日期、会员失效日期要求:统计每个月的有效会员数量(只要会员在当月任意一天有效,就计入当月统计)。
示例数据:
text
1,2021-01-01,2022-01-01
2,2021-02-02,2022-02-02
3,2021-03-03,2022-03-03
二、第一步:建表 + 导入数据
1. 创建会员表
hive
create table t_consumer(
consumerid string, -- 会员ID
startdate string, -- 生效日期
enddate string -- 失效日期
)
row format delimited
fields terminated by ','; -- 逗号分隔
2. 加载本地数据到表
hive
load data local inpath '/home/hivedata/consumer.txt' into table t_consumer;
3. 验证数据
hive
select * from t_consumer;
三、核心思路
- 一行变多行:把一个会员的
生效-失效时间段,拆成每一个有效月份(用UDTF实现) - 分组统计:按月份分组,count 会员 ID,得到每月有效会员数
四、方案 1:自定义 GenericUDTF(面试重点)
1. Maven 依赖
xml
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>3.1.2</version>
<scope>provided</scope>
</dependency>
2. 日期工具类:生成有效月份列表
java
运行
package com.qyh;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
public class DateUtils {
// 格式化日期:Jan-2021 → Jan-21
public static String getNewStr(String date){
String[] arr = date.split("-");
String yearSuffix = arr[1].substring(2); // 截取年份后两位
return arr[0] + yearSuffix;
}
// 核心:根据开始、结束日期,生成所有有效月份
public static List<String> getAllDate(String beginDate, String endDate) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdfMonth = new SimpleDateFormat("MMM-yyyy", Locale.ENGLISH);
Calendar startCal = Calendar.getInstance();
startCal.setTime(sdf.parse(beginDate));
Calendar endCal = Calendar.getInstance();
endCal.setTime(sdf.parse(endDate));
List<String> monthList = new ArrayList<>();
// 循环:逐月增加,直到结束日期
while (endCal.after(startCal) || endCal.equals(startCal)) {
String monthStr = sdfMonth.format(startCal.getTime());
monthList.add(getNewStr(monthStr));
// 月份+1
startCal.add(Calendar.MONTH, 1);
}
return monthList;
}
}
3. 自定义 UDTF 函数:炸裂日期
java
运行
package com.qyh;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import java.util.ArrayList;
import java.util.List;
public class DateExplodeUDTF extends GenericUDTF {
// 初始化:定义输出列名、类型
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
List<String> fieldNames = new ArrayList<>();
List<ObjectInspector> fieldOIs = new ArrayList<>();
// 输出列:月份
fieldNames.add("month");
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
// 核心处理逻辑
@Override
public void process(Object[] objects) throws HiveException {
// 获取入参:开始日期、结束日期
String startDate = objects[0].toString();
String endDate = objects[1].toString();
try {
// 获取所有有效月份
List<String> monthList = DateUtils.getAllDate(startDate, endDate);
// 逐行输出
for (String month : monthList) {
forward(new String[]{month});
}
} catch (Exception e) {
throw new HiveException("日期处理失败", e);
}
}
@Override
public void close() throws HiveException {}
}
五、Hive 中使用自定义函数
1. 打包上传
IDEA 打包 jar → 上传到 Linux:/opt/installs/hive/lib/xxx.jar
2. Hive 客户端添加 jar + 创建临时函数
hive
-- 添加jar包
add jar /opt/installs/hive/lib/MyFunction-1.0-SNAPSHOT.jar;
-- 创建临时函数(类全名)
create temporary function date_explode as 'com.qyh.DateExplodeUDTF';
3. 统计 SQL(带排序)
hive
-- 统计每月有效会员
select
month,
count(consumerid) as consumer_num
from t_consumer
lateral view date_explode(startdate, enddate) tmp as month
group by month
-- 按时间正确排序(面试加分项)
order by
substr(month,4,2),
case substr(month,0,3)
when 'Jan' then 1
when 'Feb' then 2
when 'Mar' then 3
when 'Apr' then 4
when 'May' then 5
when 'Jun' then 6
when 'Jul' then 7
when 'Aug' then 8
when 'Sep' then 9
when 'Oct' then 10
when 'Nov' then 11
when 'Dec' then 12
end;
六、方案 2:纯 SQL 实现(无需写 Java,更简洁)
面试中如果要求不写 UDTF,用纯 Hive SQL 也能实现:
hive
WITH
-- 1. 生成连续月份维度表(2021-01 ~ 2022-03)
month_dim AS (
SELECT
add_months('2021-01-01', idx) AS month_first_day,
date_format(add_months('2021-01-01', idx), 'MMM-yy') AS stat_month
FROM (SELECT posexplode(split(repeat(',',14),',')) AS (idx,val)) t
),
-- 2. 会员日期标准化
consumer_info AS (
SELECT
consumerid,
trunc(to_date(startdate),'MM') start_month,
trunc(to_date(enddate),'MM') end_month
FROM t_consumer
)
-- 3. 关联统计
SELECT
stat_month,
COUNT(consumerid) AS consumer_num
FROM month_dim m
LEFT JOIN consumer_info c
ON m.month_first_day BETWEEN c.start_month AND c.end_month
GROUP BY stat_month, month_first_day
ORDER BY month_first_day;
七、面试考点总结(必背)
- UDTF 是什么:一行输入,多行输出(炸裂函数)
- GenericUDTF 必须实现:
initialize()+process()+close() - 业务判断逻辑:会员当月有效条件:
会员开始日期 <= 月末 AND 会员结束日期 >= 月初 - 日期处理:
trunc截断月份、add_months月份加减 - 优化点:生成连续月份维度表,避免遗漏无会员的月份
八、预期输出结果
表格
| stat_month | consumer_num |
|---|---|
| Jan-21 | 1 |
| Feb-21 | 2 |
| Mar-21 | 3 |
| ... | ... |
| Jan-22 | 1 |
| Feb-22 | 1 |
| Mar-22 | 1 |
总结
- 这道题两种解法:Java 自定义 UDTF(面试常考)、纯 SQL(简洁高效)
- 核心逻辑:把会员周期拆成月份 → 分组统计
- 面试重点:GenericUDTF 开发流程、日期处理、lateral view 使用
更多推荐



所有评论(0)