本文继续整理 Hive 函数的下半部分内容,涵盖窗口函数、行列转换、连续登录等经典问题。 上半部分已整理单行函数与炸裂函数,可配合阅读。
一、Hive 窗口函数
1. 数据准备
创建学生表:
CREATE TABLE students(
id STRING,
name STRING,
age STRING,
gender STRING,
clazz STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION '/data/students';
创建成绩表:
CREATE TABLE scores(
id STRING,
cid STRING,
score DOUBLE
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION '/data/scores';
2. 窗口函数案例
计算每个科目分数占总分的比例
缺点: 产生两个 MapReduce,效率比较低。
SELECT
a.id,
a.cid,
a.score,
b.sum_score,
round(a.score / b.sum_score,2) AS p
FROM
bigdata.scores AS a
JOIN(
SELECT
id,
sum(score) AS sum_score
FROM
bigdata.scores
GROUP BY
id
) AS b
ON a.id = b.id;
使用 sum() over():
SELECT
id,
cid,
score,
sum_score,
round(score / sum_score,2) AS p
FROM
(
SELECT
id,
cid,
score,
sum(score) over(partition BY id) AS sum_score
FROM
bigdata.scores
) AS a;
使用 max() over():
SELECT
id,
cid,
score,
max(score) over(partition BY id) AS max_score
FROM
bigdata.scores;
使用 count() over():
SELECT
id,
cid,
score,
count(1) over(partition BY id) AS max_score
FROM
bigdata.scores;
3. 窗口内排序
累积求和:
SELECT
id,
cid,
score,
sum(score) over(partition BY id ORDER BY cid ASC) AS sum_score
FROM scores;
逐个比较求最大值:
SELECT
id,
cid,
score,
max(score) over(partition BY id ORDER BY cid) AS max_score
FROM scores;
逐个求平均值:
SELECT
id,
cid,
score,
avg(score) over(partition BY id ORDER BY cid) AS num
FROM scores;
4. 排序类窗口函数
1. row_number
组内排序:
SELECT
id,
cid,
score,
row_number() over(partition BY id ORDER BY score DESC) AS rn
FROM scores;
全局排序 ------ 求 TopN 问题:
SELECT
id,
sum(score) AS sum_score,
row_number() over(ORDER BY sum(score) DESC) AS rn -- 不能使用别名
FROM scores
GROUP BY id;
2. rank
SELECT
id,
sum(score) AS sum_score,
rank() over(ORDER BY sum(score) DESC) AS rk
FROM scores
GROUP BY id;
3. dense_rank
SELECT
id,
sum(score) AS sum_score,
dense_rank() over(ORDER BY sum(score) DESC) AS dr
FROM scores
GROUP BY id;
5. 取值类窗口函数
lag:取前面的数据,没有则补 0
SELECT
id,
sum(score) AS sum_score,
lag(sum(score),1,0) over(ORDER BY sum(score) DESC) AS last_score
FROM scores
GROUP BY id;
lead:取后面的数据
SELECT
id,
sum(score) AS sum_score,
lead(sum(score),1,0) over(ORDER BY sum(score) DESC) AS lead_score
FROM scores
GROUP BY id;
first_value:取窗口内某一列的第一个值
SELECT
id,
score,
first_value(score) over(partition BY id ORDER BY score DESC) AS fv_score
FROM scores;
last_value:取窗口内某一列的最后一个值
SELECT
id,
score,
last_value(score) over(partition BY id ORDER BY score DESC) AS lv_score
FROM scores;
二、经典问题一:行列转换
行列转换是 Hive 中非常常见的需求,主要分两大类:
- 行转列:多行 → 一列(聚合/拼接)
- 列转行 :一列/多列 → 多行(炸开/展开) Hive 中行列转换的基本原理是通过特定的内置函数实现,将一列数据展开成多行(列转行)或将多行数据聚合到一列(行转列)。
表1
姓名,科目,分数
name,item,score
张三,数学,33
张三,英语,77
李四,数学,66
李四,英语,78
CREATE TABLE table1(
name STRING,
item STRING,
score DOUBLE
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;
LOAD DATA LOCAL INPATH '/root/sh_code/table1.csv' OVERWRITE INTO TABLE bigdata.table1;
表2
姓名,数学,英语
name,math,english
张三,33,77
李四,66,78
需求:将表1转换为表2(多行转多列:行转列)
使用 CASE WHEN + MAX / SUM。
CREATE TABLE table2 AS
SELECT
name,
-- 汇总数学成绩:如果 item 等于 数学,取 score 成绩,否则取 0,再按学生求和
sum(
CASE
WHEN item = '数学' THEN score
ELSE 0
END
) AS math,
-- 汇总英语成绩:如果 item 等于 英语,取 score 成绩,否则取 0,再按学生求和
sum(
CASE
WHEN item = '英语' THEN score
ELSE 0
END
) AS english
FROM table1
GROUP BY name;
需求:将表2转换为表1(列转行)
SELECT
name,
item,
score
FROM
table2,
-- 使用 lateral view explode,将每个学生的数学和英语转换成多行记录
LATERAL VIEW explode(
map(
'数学',math, -- map 的 key 为数学,value 33.0,66.0
'英语',english -- map 的 key 为英语,value 77.0,78.0
)
) T AS item,score;
三、经典问题二:连续登录
数据
user_id,login_date
01,2021-02-28
01,2021-03-01
01,2021-03-02
01,2021-03-04
01,2021-03-05
01,2021-03-06
01,2021-03-08
02,2021-03-01
02,2021-03-02
02,2021-03-03
02,2021-03-04
02,2021-03-06
03,2021-03-06
CREATE TABLE user_login_log(
user_id STRING,
login_date STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;
LOAD DATA LOCAL INPATH '/root/sh_code/user_login_log.csv' OVERWRITE INTO TABLE bigdata.user_login_log;
需求:连续登录天数超过三天的用户
解法:利用日期与序号的差值分组(窗口函数 row_number)
SELECT
user_id, -- 用户 id
-- 连续登录开始时间
min(login_date) AS min_login_date,
-- 连续登录结束时间
max(login_date) AS max_login_date,
-- 用 login_date 与 row_number 的差值作为分组依据(相同的差值表示连续)
date_sub(login_date,rank) AS sub_date,
-- 求连续登录统计
count(1) AS num
FROM(
SELECT
user_id,
login_date,
-- 每个用户的登录记录按日期进行排序,并生成序号
row_number() over(partition BY user_id ORDER BY login_date) AS rank
FROM
user_login_log
) AS a
GROUP BY user_id,date_sub(login_date,rank)
HAVING num >= 3;
解法2:窗口函数 + 分组标记
注:原文件在此处结束,未给出解法2的具体代码。
四、总结(下)
本文整理了 Hive 函数的下半部分内容,包括:
- 窗口函数的基本用法与案例;
- 计算科目分数占比的两种方式;
- 窗口内排序、累积求和、逐个比较求最大值/平均值;
- 排序类窗口函数:
row_number、rank、dense_rank; - 取值类窗口函数:
lag、lead、first_value、last_value; - 经典问题一:行列转换(行转列、列转行);
- 经典问题二:连续登录(利用日期与序号差值分组)。
结合上半部分的单行函数与炸裂函数,可以较完整地掌握 Hive 常用函数与典型分析场景。