• hive窗口函数(开窗函数)


    一、【窗口函数概述】

    窗口函数(Window functions)是一种SQL函数,非常适合于数据分析,因此也叫做OLAP函数,其最大特点是:输入值是从SELECT语句的结果集中的一行或多行的“窗口”中获取的。你也可以理解为窗口有大有小(行有多有少)。
    通过OVER子句,窗口函数与其他SQL函数有所区别。如果函数具有OVER子句,则它是窗口函数。如果它缺少OVER子句,则它是一个普通的聚合函数。
    窗口函数可以简单地解释为类似于聚合函数的计算函数,但是通过GROUP BY子句组合的常规聚合会隐藏正在聚合的各个行,最终输出一行,窗口函数聚合后还可以访问当中的各个行,并且可以将这些行中的某些属性添加到结果集中。
    在这里插入图片描述
    通过一下案例来初步体验窗口函数

    ----sum+group by普通常规聚合操作------------
    select sum(salary) as total from employee group by dept;
    
    ----sum+窗口函数聚合操作------------
    select id,name,deg,salary,dept,sum(salary) over(partition by dept) as total from employee;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    二、窗口函数语法

    Function(arg1,..., argn) OVER ([PARTITION BY <...>] [ORDER BY <....>] [<window_expression>])
    
    --其中Function(arg1,..., argn) 可以是下面分类中的任意一个
        --聚合函数:比如sum max avg等
        --排序函数:比如rank row_number等
        --分析函数:比如lead lag first_value等
    
    --OVER [PARTITION BY <...>] 类似于group by 用于指定分组  每个分组你可以把它叫做窗口
    --如果没有PARTITION BY 那么整张表的所有行就是一组
    
    --[ORDER BY <....>]  用于指定每个分组内的数据排序规则 支持ASC、DESC
    
    --[<window_expression>] 用于指定每个窗口中 操作的数据范围 默认是窗口中所有行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三、举例:网站用户页面浏览次数分析

    在网站访问中,经常使用cookie来标识不同的用户身份,通过cookie可以追踪不同用户的页面访问情况,有下面两份数据:
    在这里插入图片描述
    字段含义:cookieid 、访问时间、pv数(页面浏览数)
    在这里插入图片描述
    字段含义:cookieid、访问时间、访问页面url
    在Hive中创建两张表表,把数据加载进去用于窗口分析。

    ---建表并且加载数据
    create table website_pv_info(
       cookieid string,
       createtime string,   --day
       pv int
    ) row format delimited
    fields terminated by ',';
    
    create table website_url_info (
        cookieid string,
        createtime string,  --访问时间
        url string       --访问页面
    ) row format delimited
    fields terminated by ',';
    
    
    load data local inpath '/root/hivedata/website_pv_info.txt' into table website_pv_info;
    load data local inpath '/root/hivedata/website_url_info.txt' into table website_url_info;
    
    select * from website_pv_info;
    select * from website_url_info;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.1 窗口聚合函数
    这里以sum()函数为例,其他聚合函数使用类似

    -----窗口聚合函数的使用-----------
    --1、求出每个用户总pv数  sum+group by普通常规聚合操作
    select cookieid,sum(pv) as total_pv from website_pv_info group by cookieid;
    
    --2、sum+窗口函数 总共有四种用法 注意是整体聚合 还是累积聚合
    --sum(...) over( )对表所有行求和
    --sum(...) over( order by ... ) 连续累积求和
    --sum(...) over( partition by... ) 同组内所有行求和
    --sum(...) over( partition by... order by ... ) 在每个分组内,连续累积求和
    
    --需求:求出网站总的pv数 所有用户所有访问加起来
    --sum(...) over( )对表所有行求和
    select cookieid,createtime,pv,
           sum(pv) over() as total_pv
    from website_pv_info;
    
    --需求:求出每个用户总pv数
    --sum(...) over( partition by... ),同组内所行求和
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid) as total_pv
    from website_pv_info;
    
    --需求:求出每个用户截止到当天,累积的总pv数
    --sum(...) over( partition by... order by ... ),在每个分组内,连续累积求和
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid order by createtime) as current_total_pv
    from website_pv_info;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    在这里插入图片描述
    3.2 窗口表达式
    我们知道,在sum(…) over( partition by… order by … )语法完整的情况下,进行的累积聚合操作,默认累积聚合行为是:从第一行聚合到当前行。
    Window expression窗口表达式给我们提供了一种控制行范围的能力,比如向前2行,向后3行。
    语法如下:

    关键字是rows between,包括下面这几个选项
    - preceding:往前
    - following:往后
    - current row:当前行
    - unbounded:边界
    - unbounded preceding 表示从前面的起点
    - unbounded following:表示到后面的终点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    ---窗口表达式
    --第一行到当前行
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid order by createtime rows between unbounded preceding and current row) as pv2
    from website_pv_info;
    
    --向前3行至当前行
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid order by createtime rows between 3 preceding and current row) as pv4
    from website_pv_info;
    
    --向前3行 向后1行
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid order by createtime rows between 3 preceding and 1 following) as pv5
    from website_pv_info;
    
    --当前行至最后一行
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid order by createtime rows between current row and unbounded following) as pv6
    from website_pv_info;
    
    --第一行到最后一行 也就是分组内的所有行
    select cookieid,createtime,pv,
           sum(pv) over(partition by cookieid order by createtime rows between unbounded preceding  and unbounded following) as pv6
    from website_pv_info;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.3窗口排序函数
    窗口排序函数用于给每个分组内的数据打上排序的标号。注意窗口排序函数不支持窗口表达式。总共有4个函数需要掌握:
    【row_number】 在每个分组中,为每行分配一个从1开始的唯一序列号,递增,不考虑重复
    【rank:】 在每个分组中,为每行分配一个从1开始的序列号,考虑重复,挤占后续位置
    【dense_rank:】 在每个分组中,为每行分配一个从1开始的序列号,考虑重复,不挤占后续位置;

    -----窗口排序函数
    SELECT
        cookieid,
        createtime,
        pv,
        RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1,
        DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2,
        ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3
    FROM website_pv_info
    WHERE cookieid = 'cookie1';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    上述这三个函数用于分组TopN的场景非常适合。

    --需求:找出每个用户访问pv最多的Top3 重复并列的不考虑
    SELECT * from
    (SELECT
        cookieid,
        createtime,
        pv,
        ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS seq
    FROM website_pv_info) tmp where tmp.seq <4;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    还有一个函数,叫做ntile函数,其功能为:将每个分组内的数据分为指定的若干个桶里(分为若干个部分),并且为每一个桶分配一个桶编号。
    如果不能平均分配,则优先分配较小编号的桶,并且各个桶中能放的行数最多相差1。
    有时会有这样的需求:如果数据排序后分为三部分,业务人员只关心其中的一部分,如何将这中间的三分之一数据拿出来呢?NTILE函数即可以满足。

    --把每个分组内的数据分为3桶
    SELECT
        cookieid,
        createtime,
        pv,
        NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2
    FROM website_pv_info
    ORDER BY cookieid,createtime;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    --理解:将数据根据cookieid分 根据pv倒序排序 排序之后分为3个部分 取第一部分
    SELECT * from
    (SELECT
         cookieid,
         createtime,
         pv,
         NTILE(3) OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn
     FROM website_pv_info) tmp where rn =1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    3.4窗口分析函数
    LAG(col,n,DEFAULT) 用于统计窗口内往上第n行值
    第一个参数为列名,第二个参数为往上第n行(可选,默认为1),第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL);
    LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值
    第一个参数为列名,第二个参数为往下第n行(可选,默认为1),第三个参数为默认值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL);
    FIRST_VALUE 取分组内排序后,截止到当前行,第一个值;
    LAST_VALUE 取分组内排序后,截止到当前行,最后一个值;

    -----------窗口分析函数----------
    --LAG
    SELECT cookieid,
           createtime,
           url,
           ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
           LAG(createtime,1,'1970-01-01 00:00:00') OVER(PARTITION BY cookieid ORDER BY createtime) AS last_1_time,
           LAG(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS last_2_time
    FROM website_url_info;
    
    
    --LEAD
    SELECT cookieid,
           createtime,
           url,
           ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
           LEAD(createtime,1,'1970-01-01 00:00:00') OVER(PARTITION BY cookieid ORDER BY createtime) AS next_1_time,
           LEAD(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS next_2_time
    FROM website_url_info;
    
    --FIRST_VALUE
    SELECT cookieid,
           createtime,
           url,
           ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
           FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS first1
    FROM website_url_info;
    
    --LAST_VALUE
    SELECT cookieid,
           createtime,
           url,
           ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
           LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1
    FROM website_url_info;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

  • 相关阅读:
    (二)算法分析
    【C#】抽象方法、接口、虚方法
    认识接口自动化测试
    nginx配置之温故而知新
    邮件协议SMTP、POP3和IMAP
    C++并发与多线程学习笔记--线程启动、结束,创建线程多法
    torch.eq的广播机制兼谈快速生成对角掩码
    将AI带入企业,红帽选择了开源
    python使用钉钉机器人给钉钉发送消息
    晶振不仅仅是可以振荡就够了
  • 原文地址:https://blog.csdn.net/weixin_44870066/article/details/126614310