窗口函数案例
数据准备
1)建表语句
create table order_info ( order_id string, --订单id user_id string, -- 用户id user_name string, -- 用户姓名 order_date string, -- 下单日期 order_amount int -- 订单金额 );
2)装载语句
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');
需求
1)统计每个用户截至每次下单的累积下单总额
这里使用基于行的窗口函数,起点是第一行,终点是当前行,并根据每个用户id分区,根据下单日期排序;因为需求是累计下单总额,所以窗口范围是从第一行到当前行。
-- 1)统计每个用户截至每次下单的累积下单总额 select user_id, user_name, order_id, order_date, order_amount, sum(order_amount) over(partition by user_id order by order_date rows between unbounded preceding and current row ) sum_amount from order_info;
运行结果:
2)统计每个用户截至每次下单的当月累积下单总额
这里使用了 substring 函数,它作为 partition by 后面的第二个字段,意味着先根据 user_id 分区之后再根据 日期的 1~7 位(也就是 年份-月份)进行分区,这样就巧妙的将每个用户不同的月份可以进行窗口函数的累计求和。
select user_id, user_name, order_id, order_date, order_amount, sum(order_amount) over(partition by user_id ,substring(order_date,1,7) order by order_date rows between unbounded preceding and current row ) sum_amount from order_info
运行结果:
3)统计每个用户每次下单距离上次下单相隔的天数(首次下单按0天算)
这里用到一个新的函数 datediff(),它是用来计算两个日期间隔的;这道题的核心在于使用 lag 函数来获取上一行的值 last_date,然后用 datediff 函数进行差值计算得到天数。
注意:lag 函数和 lead 函数是不需要声明窗口范围的。
-- 3)统计每个用户每次下单距离上次下单相隔的天数(首次下单按0天算) select user_id, user_name, order_id, order_date, order_amount, nvl(datediff(order_date,last_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_date from order_info )t1;
运行结果:
4)查询所有下单记录以及每个用户的每个下单记录所在月份的首/末次下单日期
这里使用了 first_value 和 last_value 函数,同样分区规则是先根据 user_id 再根据 order_date 进行分区的,其中 last_value 需要声明窗口范围,我们取从第一行到最后一行;first_value 没有声明窗口范围,则会默认按照 基于列 的窗口函数 between unbounded preceding and current row,因为我们比较的是日期,所以即使每次移动窗口也都会取小于当前日期的值。
-- 4)查询所有下单记录以及每个用户的每个下单记录所在月份的首/末次下单日期 select user_id, user_name, order_id, order_date, order_amount, first_value(order_date,false) over (partition by user_id,substring(order_date,1,7) order by order_date) first_date, last_value(order_date,false) over (partition by user_id,substring(order_date,1,7) order by order_date rows between unbounded preceding and unbounded following) last_date from order_info;
运行结果:
5)为每个用户的所有下单记录按照订单金额进行排名
这里使用 row_number() ,order by 是根据 order_amount 进行排序,排名的结果也是按照 order_amount 排名。
-- 5)为每个用户的所有下单记录按照订单金额进行排名 select user_id, user_name, order_id, order_date, order_amount, row_number() over (partition by user_id order by order_amount) rank from order_info;
运行结果: