常见的窗口函数汇总

老规矩,还没看前面的文章而且对这些知识点不熟悉的,可以点击连接进行观看哦:

hive从入门到放弃(一)——初识hive
hive从入门到放弃(二)——DDL数据定义
hive从入门到放弃(三)——DML数据操作
hive从入门到放弃(四)——分区与分桶

话不多说,直接开始。

什么是窗口函数

相信很多人都比较熟悉 SQL 聚合函数的语法,比如 count(), sum(), max()等,

窗口函数类似聚合函数,不同的是窗口函数不改变原有的行。

窗口函数是数据分析和数据开发必备的技能。

基本语法:<窗口函数> over (partition by <用于分组的列名> order by <用于排序的列名>)

可能这样的解释还是不明了,没事,往后看,一会你就明白了。

案例

现在先模拟几条数据,假如目前有学生成绩表(stu_scores)如下:

class(班级)id(学号)score(成绩)
100471
200398
100298
200180
200577
100680

语句一:

select *,
	rank①() over②(partition by③ class order by④ score desc) ranking 
from stu_scores;

得到的结果如下:

class(班级)id(学号)score(成绩)ranking
1002981
1006802
1004713
2003981
2001802
2005773

接下来解释一下这段 SQL,这条 SQL 的目的是求每个班级内的成绩排名

① rank() 排序的函数

② over() 指定分析函数工作的数据窗口大小

③ partition by 指定分组字段,这个案例中用 class 作为分组字段, 类似 group by

④ order by 排序,对分组后的结果进行排序

可能有些朋友会问:“这不就是 group by 和 order by 的用法么?不用窗口函数也能实现,为啥要用它?”

这是因为,单纯使用 group by 分组汇总后改变了表的行数,一行只有一个类别;

而使用窗口函数则不改变行数,可以将详细信息也展示出来。

到这,应该大致明白窗口函数的使用场景及如何使用了吧。

为了让大家更好地理解窗口函数,再写几条语句,看看结果是否和你想的一致。

语句二:

select *,
   sum(score) over(order by id) as win_sum,
   count(score) over(order by id) as win_count,
   min(score) over(order by id) as win_min
from stu_scores;

结果:

classidscorewin_sumwin_countwin_min
20018080180
100298178280
200398276380
100471347471
200577424571
100680504671

这样的结果是否和你想的一样呢?

由于不加 partition by 因此没有分组,所以从第一行开始开窗做计算。

以 win_sum 为例,第一行成绩相加 80,与第二行相加得 178,再与第三行相加得 276,以此类推。

这样做有什么意义呢?

可以每一行的数据里直观的看到,截止到本行数据,统计数据是多少。

同时可以看出每一行数据,对整体统计数据的影响。

从成绩上可能不太好理解,如果是从生产经营角度,比如对比每月营业额,可以更直观地看出差距。

其它窗口函数及关键字

rank 与 dense_rank

rank(), dense_rank() 都属于排序函数,区别在于有重复数据的时候如何排,看案例就知道

select *,
	rank() over(order by score desc) as ranking,
	dense_rank() over(order by score desc) as dense_ranking
from stu_scores;

结果:

| class | id | score | ranking | dense_ranking |
| ---- | ---- | ---- | ---- | ---- | ---- |
| 1 | 002 | 98 | 1 | 1 |
| 2 | 003 | 98 | 1 | 1 |
| 2 | 001 | 80 | 3 | 2 |
| 1 | 006 | 80 | 4 | 3 |
| 1 | 004 | 71 | 5 | 4 |
| 2 | 005 | 77 | 6 | 5 |

可以看到,遇到重复排名的时候,rank 是跳跃排序,如果有两个第一,那接下来是第三;

dense_rank()则是连续排序,如果有两个第一时,那接下来是第二。

lead 与 lag

lead(col, n, default_val):用于统计窗口内往下第 n 行值。
第一个参数为列名,第二个参数为往下第 n 行(可选,默认为1),第三个参数为默认值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL。

lag(col,n, default_val):用于统计窗口内往上第n行值,参数和 lead 一样

还是看案例吧,用文字讲确实很难讲清楚

问题:根据班级分组,统计每个班学生的成绩以及小于(大于)等于该学生成绩的上(下)一个学生的成绩:

select *,
	lead(score,1) over(partition by class order by score) as lead,
	lag(score,1) over(partition by class order by score) as lag
from stu_scores;

结果:

| class | id | score | lead | lag |
| ---- | ---- | ---- | ---- | ---- | ---- |
| 1 | 004 | 71 | 80 | null |
| 1 | 006 | 80 | 98 | 71 |
| 1 | 002 | 98 | null | 80 |
| 2 | 005 | 77 | 80 | null |
| 2 | 001 | 80 | 98 | 77 |
| 2 | 003 | 98 | null | 80 |

可以看到,第二行 lead 的结果是第三行的成绩,lag 的结果是第一行的成绩,没有的则为 null

last_value 与 first_value

这两个比较简单,顾名思义,分别表示取窗口内的最后一个值和第一条数据,但是先看看例子

select *,
	first_value(score) over(partition by class order by score) as first,
	last_value(score) over(partition by class order by score) as last
from stu_scores;

结果:

| class | id | score | first | last |
| ---- | ---- | ---- | ---- | ---- | ---- |
| 1 | 004 | 71 | 71 | 71 |
| 1 | 006 | 80 | 71 | 80 |
| 1 | 002 | 98 | 71 | 98 |
| 2 | 005 | 77 | 77 | 77 |
| 2 | 001 | 80 | 77 | 80 |
| 2 | 003 | 98 | 77 | 98 |

从结果看,first_value 的结果很合理,是每个分区的第一个数据;

但 last_value 的结果好像不符合期望,这个和我接下来要说的几个关键字有关。

UNBOUNDED、PRECEDING、FOLLOWING、CURRENT ROW

先粗略地解释一下这些关键字:

CURRENT ROW:当前行

n PRECEDING:往前 n 行数据

n FOLLOWING:往后 n 行数据

UNBOUNDED:起点

  • UNBOUNDED PRECEDING 表示从前面的起点,

  • UNBOUNDED FOLLOWING 表示到后面的终点

老规矩,先看例子

SELECT *,
  last_value(score) ov.r(PARTITION BY class ORDER BY score) last1,
  last_value(score) over(PARTITION BY class ORDER BY score RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT row) last2,
  last_value(score) over(PARTITION BY class ORDER BY score RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED following) last3,
  last_value(score) over(PARTITION BY class ORDER BY score RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING) last4,
  last_value(score) over(PARTITION BY class ORDER BY score ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) last5
FROM stu_scores;

结果:

classidscorelast1last2last3last4last5
1004717171987180
1006808080988098
1002989898989898
2005777777988080
2001808080988098
2003989898989898

用的都是 last_value 结果还不一样,为什么呢?

这是因为,last_value 默认的窗口是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

表示当前行永远是最后一个值,因此 last1 和 last2 的结果是一样的。

如果要获取每个分组的最后一个值,

则需改成 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING

表示从最前一行作为起点,最后一行为终点,就是 last3 的结果(由于两个分组排序后最后一个数都是 98,看不出区别,大家可以去验证一下)

至于 last4 和 last5 的区别,则是 RANGE 和 ROWS 的区别:

RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING:

表示当前行的值分别减 3 和 加 3,以第 4 行为例,原来的 score 是 77,各加减 3,则是 74 到 80 的范围

80 刚好是下一行的值,因此它的结果为 80,其它行由于加减 3 后没有对应的值,因此为自身。

ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING:

表示当前行分别往前往后减一行,记住,rows 和 range 的区别就在于是当前行数还是当前行的值。

因此在同一个分组内,第一行 last5 的值为下一行的值,第二行为下一行的值,以此类推,第二个分组也一样。

总结

到这,窗口函数的内容就基本上都讲完了。这些可以说是数据分析和数据开发必备的技能,因此必须要熟练。

至于如何才能熟练,还需要多实践。

码字不易,如果觉得不错,麻烦动动小手点个赞,谢谢!

持续关注不迷路,转载请注明出处!—— 大数据的奇妙冒险

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Oracle 窗口函数是一种强大的查询工具,它可以在查询结果中添加一些额外的列,这些列是根据特定的窗口定义计算出来的。常见窗口函数包括 RANK、DENSE_RANK、ROW_NUMBER、LEAD、LAG、SUM、AVG、MAX、MIN 等等。 下面是一些常用的 Oracle 窗口函数: 1. RANK() OVER (PARTITION BY ... ORDER BY ...) 该函数用于计算每个分区内的排名,相同的值会得到相同的排名,排名相同的值会得到相同的平均排名。 2. DENSE_RANK() OVER (PARTITION BY ... ORDER BY ...) 该函数与 RANK() 函数类似,但是它不会跳过排名相同的值,而是按照顺序依次分配排名。 3. ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) 该函数用于为每一行分配一个唯一的行号,不考虑分区和排序。 4. LEAD(column, n, default) OVER (PARTITION BY ... ORDER BY ...) 该函数用于获取当前行后面第 n 行的值,如果不存在则返回 default 值。 5. LAG(column, n, default) OVER (PARTITION BY ... ORDER BY ...) 该函数用于获取当前行前面第 n 行的值,如果不存在则返回 default 值。 6. SUM(column) OVER (PARTITION BY ... ORDER BY ...) 该函数用于计算每个分区内指定列的总和。 7. AVG(column) OVER (PARTITION BY ... ORDER BY ...) 该函数用于计算每个分区内指定列的平均值。 8. MAX(column) OVER (PARTITION BY ... ORDER BY ...) 该函数用于获取每个分区内指定列的最大值。 9. MIN(column) OVER (PARTITION BY ... ORDER BY ...) 该函数用于获取每个分区内指定列的最小值。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值