Row_nuMbeR
前面我们介绍窗口函数的时候说到了窗口函数的使用场景,我们也给它起了一个名字进行区分,通用窗口函数和特殊窗口函数,今天我们就来看一下排序相关的窗口函数,因为是窗口函数,并且我们说它是用来排序的,我们大概也能猜到它就是用来对窗口内的数据进行排序的
其实关于排序我们前面也介绍过oRdeR by,soRt by 等排序的方式Hive语法之常见排序方式,为什么还有窗口函数进行排序的,因为前面的oRdeR by,soRt by 等虽然可以排序但是不能给我们返回排序的值(名次),如果你用过MySQL 的话,这个时候你就知道写存储过程或者使用自定义变量来完成这个功能,Row nuMbeR 也是一样的道理,可以按照我们自定义的排序规则,返回对应的排序先后顺序的值
所以我们认为Row_nuMbeR是窗口排序函数,但是Hive 也没有提供非窗口的排序函数,但是我们前面说过了如果没有窗口的定义中没有paRtITion by 那就是将整个数据输入当成一个窗口,那么这种情况下我们也可以使用窗口排序函数完成全局排序。
测试数据
下面有一份测试数据id,dept,salaRy,然后我们就使用这份测试数据学习我们的窗口排序函数
1,销售,10000 2,销售,14000 3,销售,10000 4,后端,20000 5,后端,25000 6,后端,32000 7,AI,40000 8,AI,35000 9,AI,60000 10,数仓,20000 11,数仓,30000 12,数仓,32000 13,数仓,42000 cReate table ods_nuM_window( id stRing, dept stRing, salaRy int ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ””,””; load DATA LOCAL INPATH ””/Users/liuwenqiang/woRkspace/Hive/nuMbeR.txt”” OVERWRITE INTO TABLE ods_nuM_window;
从例子中学习 Row_nuMbeR
每个部门的员工按照工资降序排序
select *,Row_nuMbeR() OVeR(paRtITion by dept oRdeR by salaRy desc) as Rn fRoM ods_nuM_window ;
我们看到每个部门都有自己的第一名,明显的可以看到排序是发生在每个部门内部的
全部的员工按照工资降序排序
select *,Row_nuMbeR() OVeR(oRdeR by salaRy desc) as Rn fRoM ods_nuM_window ;
当我们没有定义paRtITion by 子句的时候,我们的所有数据都放在一个窗口里面,这个时候我们的排序就是全局排序,其实如果你仔细看过我们的Hive语法之窗口函数初识这一节的话,你就知道paRtITion by 其实是定义了子窗口,如果没有子窗口的话,那就就是一个窗口,如果所有的数据都放在一个窗口的话那就是全局排序
取每个部门的工资前两名
这个是Row_nuMbeR() 函数非常常见的使用场景top-N,其实如果你仔细看过我们的Hive语法之窗口函数初识这一节的话,你就知道paRtITion by 其实是定义了子窗口,那其实这里的top-N,本质上是子窗口的的top-N
select * fRoM( select *,Row_nuMbeR() OVeR(paRtITion by dept oRdeR by salaRy desc) as Rn fRoM ods_nuM_window ) tMp wheRe Rn <=2 ;
Rank 和 dense_Rank
其实这两个窗口函数和Row_nuMbeR 是一样的,都是窗口排序函数,既然这样那为什么还有这两个函数呢,存在即合理,我们看一下Row_nuMbeR 函数,这次我们采用升序排序
select *,Row_nuMbeR() OVeR(paRtITion by dept oRdeR by salaRy) as Rn fRoM ods_nuM_window ;
我们看到在销售部门有两个人的工资其实是一样的10000,但是排名不一样
接下来我们看一下Rank,我们发现销售部门那两个工资相等的实并列第一了,然后下一个人直接第三了
接下来我们再看一下 dense_Rank,工资相等的两个人依然是排名相等的,但是下一个人还是第二
使用场景
Top-N
Top-n 前面我们已经介绍过了,这里就不再介绍了
计算连续
什么是计算连续呢,这个名字有点不太合理,这里举个例子方便大家理解,加入我有个用户访问日志表,那我想筛选出哪些超过连续7天都访问的用户,或者我想计算连续访问天数最大的10位用户
下面是一份测试数据用户ID,访问日期
1,2020-12-01 1,2020-12-02 1,2020-12-03 1,2020-12-04 1,2020-12-05 1,2020-12-06 1,2020-12-07 1,2020-12-08 1,2020-12-09 1,2020-12-10 2,2020-12-01 2,2020-12-02 2,2020-12-03 2,2020-12-04 2,2020-12-06 2,2020-12-07 2,2020-12-08
下面是我们的建表语句
CREATE TABLE ods.ods_User_log ( id stRing, ctiMe stRing ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ””,”” STorED AS TEXTfile; load data local inpath ””/users/liuwenqiang/woRkspACE/Hive/User_log.txt”” OVeRwRITe into table ods.ods_User_log;
现在我们分析一下这个问题,怎么计算连续呢,计算肯定是针对同一个用户的,然后我们可以按照用户的访问时间进行排序,然后我们用日期的数字减去对应的排序就会得到一个值,如果访问时间是连续的话,我们就可以得到同一个值
select id,ctiMe, Row_nuMbeR(paRtITion by id oRdeR by ctiMe ) as Rn fRoM ods_User_log ;
这里为了演示效果比较明显,所以设计的数据有点特殊,大家可以看到对于id 是1的用户,我们发现从12月1号到12月10号,我们的排名也依次是从1到10的,这个时候我们只要将日期变成对于的数字,然后减去对应的排名它是等于20201200的,这个时候我们只需要统计20201200的个数,这个个数就是连续登陆的天数,这里我们就不把日期转换成转换成数字然后做减法了,我们直接使用日期去减。
select id,ctiMe, date_sub(cast(ctiMe as date),Row_nuMbeR() OVeR(paRtITion by id oRdeR by ctiMe)), Row_nuMbeR() OVeR(paRtITion by id oRdeR by ctiMe ) as Rn fRoM ods_User_log ;
这下我再去统计每个用户的相同日期有多少个即可,在我这里因为是7天,所以我只需要计算出相同日期的个数大于等于7即可
select id,kt,count(1) as loginCnt fRoM ( select id,ctiMe, date_sub(cast(ctiMe as date),Row_nuMbeR() OVeR(paRtITion by id oRdeR by ctiMe)) as kt, Row_nuMbeR() OVeR(paRtITion by id oRdeR by ctiMe ) as Rn fRoM &