1、炸裂函数
1.1、UDTF概述
定义:
UDTF(Table-Generating Functions),接收一行数据,输出一行或多行数据。
1.1.1、explode(ARRAY a)
功能:
语法:
select explode(array("a","b","c")) as item;
1.1.2、explode(Map m)
功能:
语法:
select explode(map("a",1,"b",2,"c",3)) as (key,value);
1.1.3、posexplode(ARRAY a)
功能:
语法:
select posexplode(array("a","b","c")) as (pos,item);
1.1.4、inline(ARRAYf1:T1,...,fn:Tn> a)
功能:
语法:
select inline(array(named_struct("id", 1, "name", "zs"), named_struct("id", 2, "name", "ls"),
named_struct("id", 3, "name", "ww"))) as (id, name);
1.2、Lateral View
定义:
Latera View 通常与UDTF配合使用。Lateral View可以将UDTF应用到源表的每行数据,将每行数据转换为一行或多行,并将源表中每行的输出结果与该行连接起来,形成一个虚拟表。
语法:
select id, name, hobbies, hobby
from person lateral view explode(hobbies) tmp as hobby;
2、案例演示
2.1、数据准备
1)表结构
movie | category |
《疑犯追踪》 | 悬疑,动作,科幻,剧情 |
《Lie to me》 | 悬疑,警匪,动作,心理,剧情 |
《战狼2》 | 战争,动作,灾难 |
2)建表语句
create table movie_info(
movie string, --电影名称
category string --电影分类
)
row format delimited fields terminated by "\t";
3)装载语句
insert overwrite table movie_info
values ("《疑犯追踪》", "悬疑,动作,科幻,剧情"),
("《Lie to me》", "悬疑,警匪,动作,心理,剧情"),
("《战狼2》", "战争,动作,灾难");
2.2、需求
1)需求说明
根据上述电影信息表,统计各分类的电影数量,期望结果如下:
剧情 | 2 |
动作 | 3 |
心理 | 1 |
悬疑 | 2 |
战争 | 1 |
灾难 | 1 |
科幻 | 1 |
警匪 | 1 |
2)答案
select cate, count(*)
from (
select movie, cate
from (
select movie, split(category, ',') cates
from movie_info) t1 lateral view explode(cates) tmp as cate) t2
group by cate;
3、窗口函数(开窗函数)
3.1、概述
定义:
窗口函数,能为每行数据划分一个窗口,然后对窗口范围内的数据进行计算,最后将计算结果返回给该行数据。
语法:
窗口函数的语法中主要包括“窗口”和“函数”两部分。其中“窗口”用于定义计算范围,“函数”用于定义计算逻辑。
基本语法如下:
select order_id, order_date, amount, 函数(amount) over (窗口范围) total_amount
from order_info;
语法——函数
绝大多数的聚合函数都可以配合窗口使用,例如max(),min(),sum(),count(),avg()等。
select order_id, order_date, amount, sum(amount) over (窗口范围) total_amount
from order_info;
语法——窗口
窗口范围的定义分为两种类型,一种是基于行的,一种是基于值的。
数据准备
DROP TABLE IF EXISTS order_info;
create table order_info(
`order_id` string COMMENT '订单id',
`user_id` string COMMENT '用户id',
`order_date` string COMMENT '下单日期',
`amount` decimal(16, 2) COMMENT '订单金额'
) COMMENT '订单表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
insert overwrite table order_info
values ('1', '1001', '2022-01-01', 10),
('2', '1001', '2022-01-02', 20),
('3', '1001', '2022-01-03', 10),
('4', '1002', '2022-01-04', 30),
('5', '1002', '2022-01-05', 40),
('6', '1002', '2022-01-06', 20);
3.1.1、窗口—基于行
思考:如下基于行的窗口划分方式,查询语句的返回结果是什么?
select order_id,
user_id,
order_date,
amount,
sum(amount) over (order by order_date rows between unbounded preceding and current row) total_amount
from order_info;
3.1.2、窗口—基于值
思考:如下基于值的窗口划分方式,是基于哪个字段的值划分的?该查询语句的返回结果是什么?
select order_id,
user_id,
order_date,
amount,
sum(amount) over (order by order_date range between unbounded preceding and current row) total_amount
from order_info;
3.1.3、窗口—分区
定义窗口范围时,可以指定分区字段,每个分区单独划分窗口。
分区语法如下,请思考:该查询语句的结果是什么?
select order_id,
user_id,
order_date,
amount,
sum(amount)
over (partition by user_id order by order_date rows between unbounded preceding and current row) total_amount
from order_info;
3.1.4、窗口—缺省
over( ) 中的三部分内容partition by、order by、(rows|range) between … and … 均可省略不写。
partition by省略不写,表示不分区
order by 省略不写,表示不排序
(rows|range) between … and … 省略不写,则使用其默认值,默认值如下:
若over()中包含order by,则默认值为
range between unbounded preceding and current row
若over()中不包含order by,则默认值为
rows between unbounded preceding and unbounded following
分区语法如下,请思考:该查询语句的结果是什么?
select order_id, user_id, order_date, amount, sum(amount) over (partition by user_id order by order_date) total_amount
from order_info;
3.2、常用窗口函数
按照功能,常用窗口可划分为如下几类:聚合函数、跨行取值函数、排名函数。
1)聚合函数
max:最大值。
min:最小值。
sum:求和。
avg:平均值。
count:计数。
2)跨行取值函数
(1)lead和lag
功能:获取当前行的上/下边某行、某个字段的值。
语法:
select order_id,
user_id,
order_date,
amount,
lag(order_date, 1, '1970-01-01')
over (partition by user_id order by order_date) last_date,
lead(order_date, 1, '9999-12-31') over (partition by user_id order by order_date) next_date
from order_info;
思考:该查询语句的结果是什么?
注:lag和lead函数不支持自定义窗口。
(2)first_value和last_value
功能:获取窗口内某一列的第一个值/最后一个值
语法:
select order_id,
user_id,
order_date,
amount,
first_value(order_date, false) over (partition by user_id order by order_date) first_date,
last_value(order_date, false) over (partition by user_id order by order_date) last_date
from order_info;
思考:该查询语句的结果是什么?
3)排名函数
常用窗口函数——rank、dense_rank、row_number
功能:计算排名
select stu_id,
course,
score,
rank() over (partition by course order by score desc) rk,
dense_rank() over (partition by course order by score desc) dense_rk,
row_number() over (partition by course order by score desc) rn
from score_info;
注:rank 、dense_rank、row_number不支持自定义窗口。
4、案例演示
4.1、数据准备
1)表结构
order_id | user_id | user_name | order_date | order_amount |
1 | 1001 | 小元 | 2022-01-01 | 10 |
2 | 1002 | 小海 | 2022-01-02 | 15 |
3 | 1001 | 小元 | 2022-02-03 | 23 |
4 | 1002 | 小海 | 2022-01-04 | 29 |
5 | 1001 | 小元 | 2022-01-05 | 46 |
2)建表语句
DROP TABLE IF EXISTS order_info;
create table order_info
(
order_id string, --订单id
user_id string, -- 用户id
user_name string, -- 用户姓名
order_date string, -- 下单日期
order_amount int -- 订单金额
);
3)装载语句
insert overwrite table order_info
values ('1', '1001', '小元', '2022-01-01', '10'),
('2', '1002', '小海', '2022-01-02', '15'),
('3', '1001', '小元', '2022-02-03', '23'),
('4', '1002', '小海', '2022-01-04', '29'),
('5', '1001', '小元', '2022-01-05', '46'),
('6', '1001', '小元', '2022-04-06', '42'),
('7', '1002', '小海', '2022-01-07', '50'),
('8', '1001', '小元', '2022-01-08', '50'),
('9', '1003', '小辉', '2022-04-08', '62'),
('10', '1003', '小辉', '2022-04-09', '62'),
('11', '1004', '小猛', '2022-05-10', '12'),
('12', '1003', '小辉', '2022-04-11', '75'),
('13', '1004', '小猛', '2022-06-12', '80'),
('14', '1003', '小辉', '2022-04-13', '94');
4.2、需求
1)统计每个用户截至每次下单的累积下单总额
(1)期望结果
order_id | user_id | user_name | order_date | order_amount | sum_so_far |
1 | 1001 | 小元 | 2022-01-01 | 10 | 10 |
5 | 1001 | 小元 | 2022-01-05 | 46 | 56 |
8 | 1001 | 小元 | 2022-01-08 | 50 | 106 |
3 | 1001 | 小元 | 2022-02-03 | 23 | 129 |
6 | 1001 | 小元 | 2022-04-06 | 42 | 171 |
2 | 1002 | 小海 | 2022-01-02 | 15 | 15 |
4 | 1002 | 小海 | 2022-01-04 | 29 | 44 |
7 | 1002 | 小海 | 2022-01-07 | 50 | 94 |
9 | 1003 | 小辉 | 2022-04-08 | 62 | 62 |
10 | 1003 | 小辉 | 2022-04-09 | 62 | 124 |
12 | 1003 | 小辉 | 2022-04-11 | 75 | 199 |
14 | 1003 | 小辉 | 2022-04-13 | 94 | 293 |
11 | 1004 | 小猛 | 2022-05-10 | 12 | 12 |
13 | 1004 | 小猛 | 2022-06-12 | 80 | 92 |
(2)需求实现
select order_id,
user_id,
user_name,
order_date,
order_amount,
sum(order_amount)
over (partition by user_id order by order_date rows between unbounded preceding and current row ) sum_so_far
from order_info;
2)统计每个用户截至每次下单的当月累积下单总额
(1)期望结果
order_id | user_id | user_name | order_date | order_amount | sum_so_far |
1 | 1001 | 小元 | 2022-01-01 | 10 | 10 |
5 | 1001 | 小元 | 2022-01-05 | 46 | 56 |
8 | 1001 | 小元 | 2022-01-08 | 50 | 106 |
3 | 1001 | 小元 | 2022-02-03 | 23 | 23 |
6 | 1001 | 小元 | 2022-04-06 | 42 | 42 |
2 | 1002 | 小海 | 2022-01-02 | 15 | 15 |
4 | 1002 | 小海 | 2022-01-04 | 29 | 44 |
7 | 1002 | 小海 | 2022-01-07 | 50 | 94 |
9 | 1003 | 小辉 | 2022-04-08 | 62 | 62 |
10 | 1003 | 小辉 | 2022-04-09 | 62 | 124 |
12 | 1003 | 小辉 | 2022-04-11 | 75 | 199 |
14 | 1003 | 小辉 | 2022-04-13 | 94 | 293 |
11 | 1004 | 小猛 | 2022-05-10 | 12 | 12 |
13 | 1004 | 小猛 | 2022-06-12 | 80 | 80 |
(2)需求实现
select order_id,
user_id,
user_name,
order_date,
order_amount,
sum(order_amount)
over (partition by user_id,date_format(order_date,'yyyy-MM') order by order_date rows between unbounded preceding and current row ) sum_so_far
from order_info;
3)统计每个用户每次下单距离上次下单相隔的天数(首次下单按0天算)
(1)期望结果
order_id | user_id | user_name | order_date | order_amount | diff |
1 | 1001 | 小元 | 2022-01-01 | 10 | 0 |
5 | 1001 | 小元 | 2022-01-05 | 46 | 4 |
8 | 1001 | 小元 | 2022-01-08 | 50 | 3 |
3 | 1001 | 小元 | 2022-02-03 | 23 | 26 |
6 | 1001 | 小元 | 2022-04-06 | 42 | 62 |
2 | 1002 | 小海 | 2022-01-02 | 15 | 0 |
4 | 1002 | 小海 | 2022-01-04 | 29 | 2 |
7 | 1002 | 小海 | 2022-01-07 | 50 | 3 |
9 | 1003 | 小辉 | 2022-04-08 | 62 | 0 |
10 | 1003 | 小辉 | 2022-04-09 | 62 | 1 |
12 | 1003 | 小辉 | 2022-04-11 | 75 | 2 |
14 | 1003 | 小辉 | 2022-04-13 | 94 | 2 |
11 | 1004 | 小猛 | 2022-05-10 | 12 | 0 |
13 | 1004 | 小猛 | 2022-06-12 | 80 | 33 |
(2)需求实现
答案一:
select order_id,
user_id,
user_name,
order_date,
order_amount,
datediff(order_date, lag(order_date, 1, order_date) over (partition by user_id order by order_date)) diff
from order_info;
答案二:
select
order_id,
user_id,
user_name,
order_date,
order_amount,
nvl(datediff(order_date,last_order_date),0) diff
from
(
select
order_id,
user_id,
user_name,
order_date,
order_amount,
lag(order_date,1,null) over(partition by user_id order by order_date) last_order_date
from order_info
)t1
4)查询所有下单记录以及每个用户的每个下单记录所在月份的首/末次下单日期
(1)期望结果
order_id | user_id | user_name | order_date | order_amount | first_date | last_date |
1 | 1001 | 小元 | 2022-01-01 | 10 | 2022-01-01 | 2022-01-08 |
5 | 1001 | 小元 | 2022-01-05 | 46 | 2022-01-01 | 2022-01-08 |
8 | 1001 | 小元 | 2022-01-08 | 50 | 2022-01-01 | 2022-01-08 |
3 | 1001 | 小元 | 2022-02-03 | 23 | 2022-02-03 | 2022-02-03 |
6 | 1001 | 小元 | 2022-04-06 | 42 | 2022-04-06 | 2022-04-06 |
2 | 1002 | 小海 | 2022-01-02 | 15 | 2022-01-02 | 2022-01-07 |
4 | 1002 | 小海 | 2022-01-04 | 29 | 2022-01-02 | 2022-01-07 |
7 | 1002 | 小海 | 2022-01-07 | 50 | 2022-01-02 | 2022-01-07 |
9 | 1003 | 小辉 | 2022-04-08 | 62 | 2022-04-08 | 2022-04-13 |
10 | 1003 | 小辉 | 2022-04-09 | 62 | 2022-04-08 | 2022-04-13 |
12 | 1003 | 小辉 | 2022-04-11 | 75 | 2022-04-08 | 2022-04-13 |
14 | 1003 | 小辉 | 2022-04-13 | 94 | 2022-04-08 | 2022-04-13 |
11 | 1004 | 小猛 | 2022-05-10 | 12 | 2022-05-10 | 2022-05-10 |
13 | 1004 | 小猛 | 2022-06-12 | 80 | 2022-06-12 | 2022-06-12 |
(2)需求实现
select order_id,
user_id,
user_name,
order_date,
order_amount,
first_value(order_date, false)
over (partition by user_id,date_format(order_date, 'yyyy-MM') order by order_date rows between unbounded preceding and unbounded following) first_date,
last_value(order_date, false)
over (partition by user_id,date_format(order_date, 'yyyy-MM') order by order_date rows between unbounded preceding and unbounded following) last_date
from order_info;
5)为每个用户的所有下单记录按照订单金额进行排名
(1)期望结果
order_id | user_id | user_name | order_date | order_amount | rk | drk | rn |
8 | 1001 | 小元 | 2022-01-08 | 50 | 1 | 1 | 1 |
5 | 1001 | 小元 | 2022-01-05 | 46 | 2 | 2 | 2 |
6 | 1001 | 小元 | 2022-04-06 | 42 | 3 | 3 | 3 |
3 | 1001 | 小元 | 2022-02-03 | 23 | 4 | 4 | 4 |
1 | 1001 | 小元 | 2022-01-01 | 10 | 5 | 5 | 5 |
7 | 1002 | 小海 | 2022-01-07 | 50 | 1 | 1 | 1 |
4 | 1002 | 小海 | 2022-01-04 | 29 | 2 | 2 | 2 |
2 | 1002 | 小海 | 2022-01-02 | 15 | 3 | 3 | 3 |
14 | 1003 | 小辉 | 2022-04-13 | 94 | 1 | 1 | 1 |
12 | 1003 | 小辉 | 2022-04-11 | 75 | 2 | 2 | 2 |
9 | 1003 | 小辉 | 2022-04-08 | 62 | 3 | 3 | 3 |
10 | 1003 | 小辉 | 2022-04-09 | 62 | 3 | 3 | 4 |
13 | 1004 | 小猛 | 2022-06-12 | 80 | 1 | 1 | 1 |
11 | 1004 | 小猛 | 2022-05-10 | 12 | 2 | 2 | 2 |
(2)需求实现
select order_id,
user_id,
user_name,
order_date,
order_amount,
rank() over (partition by user_id order by order_amount desc) rk,
dense_rank() over (partition by user_id order by order_amount desc) drk,
row_number() over (partition by user_id order by order_amount desc) rn
from order_info;