hive-窗口(分析、OLAP)函数

本文深入探讨SQL窗口函数,包括SYNTAX结构、PARTITION BY、ORDER BY、窗口定义及专用窗口和聚合函数。窗口函数提供了一种灵活的分组方式,能实现复杂逻辑,如平均值计算、排名、极值提取等。通过ROWS和RANGE定义窗口范围,可以针对行或值进行滑动。同时,文章介绍了各种窗口函数的使用场景,如AVG、COUNT、ROW_NUMBER、LEAD和LAG等。

文章目录

一、前置

所谓分析,就是复杂到简单,大多数情况,都是多变少。基本思想就是分门别类、多条数据归纳出一条结论,也就是分组聚合。
而group by一个分组只能返回一条数据,太死板。窗口函数提供了一种灵活、强大的分组方式,配合聚合函数和窗口函数,能实现复杂的逻辑。

group by + 聚合函数每组只返回一行,因为它只能对组内的所有数据进行统计。
窗口函数会针对组内每行数据的窗口进行一次聚合计算,因为窗口子句指定了数据窗口大小,这个数据窗口包含的行会随着行的变化而滑动变化,可以在这个滑动窗口里进行计算并返回一个值。

  • 即使是传统的avg,使用了窗口后也是每行返回一个结果
SELECT subject,avg(score) OVER(PARTITION BY subject) FROM scores;

在这里插入图片描述

窗口这种可以随意设定范围的分组方式比group by这种一组返回一条、只能根据列来分组要强大的多,可以做到一行对应一个结果。group by能实现的,窗口函数都能实现

二、syntax

窗口函数包含2部分,over前面的窗口函数和[over,后面]的窗口子句(用来定义窗口)
窗口子句用来定义窗口,窗口函数用来定义在这些窗口上进行什么操作

分析函数名(参数) OVER (PARTITION BY子句 ORDER BY子句 ROWS/RANGE子句)

window_function (expression) OVER (

[ PARTITION BY part_list ]

[ ORDER BY order_list ]

[ { ROWS | RANGE } BETWEEN frame_start AND frame_end ] )

1.作为窗口函数使用的函数有两类:

  • 专用窗口函数rank、dense_rank、row_number等
  • 聚合函数(sum、avg、count、max、min)

2.PARTITION BY 表示将数据先按 part_list 进行分组, 如果不指定 PARTITION BY,则不对数据进行分组,换句话说,所有数据看作同一个分组。

在这里插入图片描述

  • 其中partition by可以省略。窗口函数的partition by子句不是必须的,即使省略也可以正常使用窗口函数,只是把所有数据作为一个组
    在这里插入图片描述

3.窗口函数原则上只能写在select子句中,不用在where和group by子句中使用

4.ORDER BY 表示将各个分组内的数据按 order_list 进行排序。一般情况下都要指定,如果不指定 ORDER BY,则不对各分区做排序,通常用于那些顺序无关的窗口函数,例如SUM。order by子句中可以通过desc,asc指定降序或升序,如果省略,默认会按照升序asc排列

5.专用窗口函数括号中可以为空,不用指定参数。聚合函数作为窗口函数要指定参数。

6.窗口函数兼具分组和排序的功能,但是其分组并不具备group by子句的汇总功能,而是一行一个结论。

三、窗口的定义:WINDOW子句(灵活控制窗口)

待分析的数据是一个黑箱,开个窗口向外暴露,窗口可以滑动。
既然是滑动的,那就需要设置是按行滑动,还是根据对应列的值滑动,也就是锚点(滑轨)
既然是窗口那就需要设置窗口的大小,也就是步长。

1.步长

frame_extent指定帧的起止点,在其中也可以只用frame_start(结束位置就默认为当前行)和frame_between指定起点和终点。

frame_start和frame_end可以是以下几种:
CURRENT ROW: 当前行
UNBOUNDED:起点(一般结合PRECEDING,FOLLOWING使用)
UNBOUNDED PRECEDING 表示该窗口最前面的行(起点)
UNBOUNDED FOLLOWING:表示该窗口最后面的行(终点)
N PRECEDING: 当前行之前的N行,可以是数字,也可以是一个能计算出数字的表达式
N FOLLOWING:当前行之后的N行,可以是数字,也可以是一个能计算出数字的表达式

如果没指定帧的话,默认的frame取决于ORDER BY。
如果有ORDER BY,SQL会默认帧是区间内从第一行(UNBOUNDED PRECEDING)到当前行(CURRENTROW)
如果没有ORDER BY,SQL会默认帧是区间内从第一行(UNBOUNDED PRECEDING)到最后一行(UNBOUNDED FOLLOWING)

比如说:
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(表示从起点到当前行)
ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING(表示往前2行到往后1行)
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW(表示往前2行到当前行)
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING(表示当前行到终点)
官网有一段话列出了哪些窗口函数是不支持window子句的,如下图所示:
在这里插入图片描述

2.锚点:ROWS/RANGE BETWEEN

通过ROWS与RANGE来限制窗口的行数

{1}ROWS 以行为锚点,按行的row_number来筛选行

例如 ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING 表示当前行往前数3行到往后 3 行,一共 7 行数据(或小于 7 行,如果碰到了边界)。所以ROWS是通过排序后的前后位置选取窗口范围。

{2}RANGE 根据目标列的值为滑轨,按值筛选行。表示的是具体的值,比这个值小n的行,比这个值大n的行

[1]那个根据那一列的值呢?

根据order by的列的值进行范围判断。
值相同的行会被合并成同一条数据再进行计算,相同行窗口计算时的结果也是相同的。
是否是相同行,是根据ORDER BY排序时的结果决定的。
有ORDER BY时:同行是说在ORDER BY排序时不唯一的行。【即具有相同数值的行】
不同行是说ORDER BY排序时具有不同的数值的行。
没有ORDER BY:那么就是当前分区的所有行都包含在框架中,因为所有行都会成为当前行的相同行。【特别要注意最后一句的意思】

[2]case
  • RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING
    表示选取取值在 [c-3,c+3]这个范围内的行(其中c 为当前行的值)。所以RANGE是通过数值的大小选取窗口范围。
  • range between 4 preceding AND 7 following
    如果当前值为10的话就取前后的值在6到17之间的数据。
  • partition by userid order by date range between 3 preceding and current row
    窗口大小设置为该分区内小于本记录date-3天的窗口
  • sum(close) range between 100 preceding and 200 following
    则通过字段差值来进行选择。如当前行的 close 字段值是 200,那么这个窗口大小的定义就会选择分区中 close 字段值落在 100 至 400 区间的记录(行)。

{3}参考下图看看ROWS 和 RANGE的区别

1

表数据如下
i col
1 1
2 1

SELECT t.*,
SUM(col) OVER (ORDER BY col
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as ROWSresult,
SUM(col) OVER (ORDER BY col
     RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as RANGEresult             
FROM t
i    col    rowsresult    rangeresult
1     1       1               2
2     1       2               2

ROWS子句滑到第一行时,从第一行到当前行,也就是第一行到一行,所以rowsresult是sum(1)=1。
RANGE计算值符合范围的。col两行值相同因此第一次就计算了2行。

2

一个表只有一列val,当前行val列的值是2
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW指定的窗口就是前一行和当前行,共两行;
RANGE BETWEEN 1 PRECEDING AND CURRENT ROW指定的窗口是当前行val值2减去1到2这个范围内的数据,共包括1、1.2、2三行。
在这里插入图片描述

四、专用窗口函数和聚合函数

https://zhuanlan.zhihu.com/p/336545182
https://www.jianshu.com/p/12eaf61cf6e1

按照使用功能场景,可以分为以下 5类:

聚合(Aggregate):AVG(), COUNT(), MIN(), MAX(), SUM()…

排序(Ranking):

---
ROW_NUMBER()123
RANK()112复连
DENSE_RANK()113

极值(Value):FIRST_VALUE(), LAST_VALUE()…

分组取最早、最迟函数可用于取用户的首次行为时间、最后一次行为时间,计算生命周期。

First_Value():分组内排序后,获得组内当前行往前的首个值;

Last_Value():分组内排序后,获得组内当前行往前的最后一个值。

位移(Shift):LEAD(), LAG()…

前后平移可快捷计算同比、环比值;

Lag(col, n, DEFAULT) 用于统计窗口内往上第n行值;

Lead(col, n, DEFAULT) 用于统计窗口内往下第n行值, 与LAG相反。

default省略,则默认为NULL; 不需显式进行手动分组排序,在使用函数 LAG/LEAD 时最终呈现记录为自动排序。

分析(Analytics):CUME_DIST、PERCENT_RANK…

Cume_Dist:小于等于当前值的行数/分组内总行数。应用场景统计,用于收入订单前多少的排名,或者订单数 pk 百分比。注意:没有 partition ,所有数据分到同一组。

Percent_Rank:分组内当前行的RANK值-1/分组内总行数-1。暂时没有想到应用场景。

分箱(Binning):

NTILE(n)

将指定窗口/子窗口中的数据均分为n份,多出的那一行放在第一个分区。
用于求TOP百分比或几分之几
不支持ROWS BETWEEN

select cookieid,createtime,pv,
ntile(3) over(partition by cookieid order by pv desc) as rn
from cookie
where rn = 1;

五、case

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值