目 录
0 需求分析
1 需求实现
2 小 结
有如下数据表:
A | 2015-01 | 5 |
A | 2015-01 | 15 |
B | 2015-01 | 5 |
A | 2015-01 | 8 |
B | 2015-01 | 25 |
A | 2015-01 | 5 |
A | 2015-02 | 4 |
A | 2015-02 | 6 |
B | 2015-02 | 10 |
B | 2015-02 | 5 |
A | 2015-03 | 16 |
A | 2015-03 | 22 |
B | 2015-03 | 23 |
B | 2015-03 | 10 |
B | 2015-03 | 11 |
需求如下:
每个用户截止到每月为止的最大单月访问次数和累计到该月的总访问次数
结果如下:
用户 月份 当月访问次数 最大访问次数 总访问次数
A 2015-01 33 33 33
A 2015-02 10 33 43
A 2015-03 38 38 81
B 2015-01 30 30 30
B 2015-02 15 30 45
B 2015-03 44 44 89
(1)数据准备
A,2015-01,5
A,2015-01,15
B,2015-01,5
A,2015-01,8
B,2015-01,25
A,2015-01,5
A,2015-02,4
A,2015-02,6
B,2015-02,10
B,2015-02,5
A,2015-03,16
A,2015-03,22
B,2015-03,23
B,2015-03,10
B,2015-03,11
(2)创建hive表
drop table if exists dan_test.pview
CREATE TABLE dan_test.class (
user_id string,
month string,
pv string
)
ROW format delimited FIELDS TERMINATED BY ",";
(3) 导入数据
load data local inpath "/home/centos/dan_test/pview.txt" into table pview;
(4)实现
1)核心问题剖析
从最终的需求可以看出,我们计算的结果是随着行的变化而变化,我们把这类问题称为移动计算。在hivesql中其实解决此类问题我们是通过移动窗口来解决的,类似于spark中的滑动窗口。那么控制此类行的变化范围hive中给出了具体的方法--窗口子句。
窗口:over(),分析函数如:row_number(),max(),lag()等。分析函数+窗口函数:窗口的本质就是指明了分析函数分析数据时要处理的数据范围(作用域)。窗口分为静态窗口和移动窗口(也叫滑动窗口),静态窗口指分析数据的范围是固定不变的。滑动窗口指按照行的变化,窗口数据也随着变换,不同的行对应着不同的窗口数据(类似于与spark中的滑动窗口,随着时间的变化,窗口数据也发生着变化)。窗口也是SQL编程的思维本质,就是对范围内的数据进行处理。
窗口子句:窗口函数包括三个窗口子句。分组:partition by;排序:order by;窗口大小:rows.使用语法如下:
over(partition by xxx order by yyy rows between zzz)
窗口子句范围大小的控制:
rows或(range)子句往往来控制窗口边界范围的,其语法如下:
ROWS between CURRENT ROW | UNBOUNDED PRECEDING | [num] PRECEDING AND UNBOUNDED FOLLOWING | [num] FOLLOWING| CURRENT ROW
或
RANGE between [num] PRECEDING AND [num]FOLLOWING
如下图:
注意:
rows:rows是真实的行数,也就是我们实际中所说的1,2,3...连续的行数。
range:range是逻辑上的行数,所谓的逻辑行指的就是需要通过计算才能知道是哪一行。range后面跟计算表达式,对order by后面的某个字段值进行计算,计算后的结果表示其真正的范围。(逻辑偏移量构成)。
两者区别如下:
id 列
1
1
3
6
6
6
7
8
9
分析下面两个语句:
SUM(ID) over(ORDER BY ID ROWS BETWEEN 1 preceding AND 2 following) rows_sum
SUM(ID) over(ORDER BY ID RANGE BETWEEN 1 preceding AND 2 following) range_sum
第一个为物理上的rows:表示从当前行为参考点,数据范围为前一行与后两行范围内求得的结果。数据范围为:
当前行为第一行时:数据范围如下图所示
sum(id)=1+1+3=5
当前行为第二行时:数据范围如下图所示
sun(id)=1+1+3+6=11
当前行为第三行时:数据范围如下图所示
sum(id) = 1+3 +6 +6=16
......
整个过程如下图所示:
整个窗口的变化过程就像按照每一行进行移动,移动的数据范围由窗口子句指定
第二个为逻辑上的range:数据的范围需要按照id进行计算。计算公式为:
RANGE BETWEEN 1 preceding AND 2 following。翻译为:当前行的值(此处为id的值,具体是以order by 后字段进行计算的)id-1= 当为第一行时:id=1,计算公式为id-1= sum(id)=1+1+3=5 当为第二行时:id = 1,计算同上,0= 当为第三行时:id=3,计算公式为id-1= sum(id)=3 当为第四行时:id=6, 计算公式为id-1= sum(id) = 6+6+6+7+8=33 依次类推,计算出其他行。也就是按照物理行去移动,只不过窗口的数据范围不是物理行,而是需要计算,计算所得的值的范围所在的行。 窗口函数几点认识如下: a 当窗口函数over()出现分组(partition by)子句时: unbounded preceding即第一行是指表中一个分组里的第一行, unbounded following即最后一行是指表中一个分组里的最后一行; b 当开窗函数over()无分组(partition by)子句时 unbounded preceding即第一行是指表中的第一行, unbounded following即最后一行是指表中的最后一行。 c 而无论是否省略分组子句,以下结论都是成立的: 总结如下: d 窗口函数中的分组与group by的区别: e 窗口函数执行顺序及使用规则 2)每个用户截止到每月为止的最大单月访问次数和累计到该月的总访问次数 本题就是典型的移动计算问题,从给的原始数据可以看出需要先对用户的每月的访问次数进行汇总。SQL代码如下: 按用户分组,对用户组内的数据进行窗口滑动,用户组内按照行向下移动,滑动的范围为:从第一条数据到当前行的数据范围。在数据范围内求最大值及累计sum值,这样求出的就是截止当前每月最大访问次数,及截止当前每月的总访问次数。 因而可得出规律:凡是题目中有截止当前字样的肯定需要移动计算,窗口函数去完成。 具体SQL如下: 本文通过案例来引出对窗口函数的认识,总结了窗口函数的用法及使用规律,该案例主要是对窗口函数在移动计算中的应用,类似于滑动窗口,所谓的滑动窗口也就是指每一行对应对应的数据窗口都不同,通过窗口子句类实现移动计算时数据的范围,也就是窗口每次按行滑动时长度大小,但窗口中每一次对应的数据总是在变化。通过本文你可以获得如下知识: 参考链接:https://blog.csdn.net/kuodannie1668/article/details/79757186range的应用场景:比如有一张员工薪资表。我想知道比当前员工薪资高1000元的员工总数。此时range就很好用。
1、窗口子句不能单独出现,必须有order by子句时才能出现。
2、当省略窗口子句时:
a) 如果存在order by则默认的窗口是unbounded preceding and current row --当前组的第一行到当前
行,即在当前组中,第一行到当前行
b) 如果没有order by则默认的窗口是unbounded preceding and unbounded following --整个组
(1)先看sql的执行顺序
1 from
2 on
3 join
4 where
5 group by
6 with
7 having
8 select
9 distinct
10 order by
11 limit
(2) 窗口函数执行顺序:窗口函数只能在select命令中和select命令之后使用,不能在where中使用,其执行
顺序是和select同级别的,位于distinct顺序之前,可以把窗口函数与分析函数结合后形成的看成select中字
段一样,也是可以取别名的,是select的一部分。和聚合函数不能在where语句中使用是一个道理。
--先整理数据
select a.user_id as user_id, a.month as month
,sum(a.pv) as pv
from pview a
group by a.user_id, a.month
select b.user_id
,b.month
,b.pv
,max(b.pv) over(partition by b.user_id order by b.month) as max_pv --此处省略了rows between unbounded preceding and current row
,sum(b.pv) over(partition by b.user_id order by b.month) as sum_pv
from
(
select a.user_id as user_id
,a.month as month
,sum(a.pv) as pv
from pview a
group by a.user_id, a.month
) a ;
2 小 结