带你快速了解SQL窗口函数

作者:黄子毅 时间:2024-01-27 19:11:38 

窗口函数形如:

表达式 OVER (PARTITION BY 分组字段 ORDER BY 排序字段)

有两个能力:

  • 当表达式为 rank() dense_rank() row_number() 时,拥有分组排序能力。

  • 当表达式为 sum() 等聚合函数时,拥有累计聚合能力。

无论何种能力,窗口函数都不会影响数据行数,而是将计算平摊在每一行。

这两种能力需要区分理解。

底表

带你快速了解SQL窗口函数

以上是示例底表,共有 8 条数据,城市1、城市2 两个城市,下面各有地区1~4,每条数据都有该数据的人口数。

分组排序

如果按照人口排序,ORDER BY people 就行了,但如果我们想在城市内排序怎么办?

此时就要用到窗口函数的分组排序能力:

带你快速了解SQL窗口函数

SELECT *, rank() over (PARTITION BY city ORDER BY people) FROM test

该 SQL 表示在 city 组内按照 people 进行排序。

其实 PARTITION BY 也是可选的,如果我们忽略它:

SELECT *, rank() over (ORDER BY people) FROM test

也是生效的,但该语句与普通 ORDER BY 等价,因此利用窗口函数进行分组排序时,一般都会使用 PARTITION BY。

各分组排序函数的差异

我们将 rank() dense_rank() row_number() 的结果都打印出来:

SELECT *,
rank() over (PARTITION BY city ORDER BY people),
dense_rank() over (PARTITION BY city ORDER BY people),
row_number() over (PARTITION BY city ORDER BY people)
FROM test

带你快速了解SQL窗口函数

其实从结果就可以猜到,这三个函数在处理排序遇到相同值时,对排名统计逻辑有如下差异:

  • rank(): 值相同时排名相同,但占用排名数字。

  • dense_rank(): 值相同时排名相同,但不占用排名数字,整体排名更加紧凑。

  • row_number(): 无论值是否相同,都强制按照行号展示排名。

上面的例子可以优化一下,因为所有窗口逻辑都是相同的,我们可以利用 WINDOW AS 提取为一个变量:

SELECT *,
rank() over wd, dense_rank() over wd, row_number() over wd
FROM test
WINDOW wd as (PARTITION BY city ORDER BY people)

累计聚合

我们之前说过,凡事使用了聚合函数,都会让查询变成聚合模式。如果不用 GROUP BY,聚合后返回行数会压缩为一行,即使用了 GROUP BY,返回的行数一般也会大大减少,因为分组聚合了。

然而使用窗口函数的聚合却不会导致返回行数减少,那么这种聚合是怎么计算的呢?我们不如直接看下面的例子:

SELECT *,
sum(people) over (PARTITION BY city ORDER BY people)
FROM test

带你快速了解SQL窗口函数

可以看到,在每个 city 分组内,按照 people 排序后进行了 累加(相同的值会合并在一起),这就是 BI 工具一般说的 RUNNGIN_SUM 的实现思路,当然一般我们排序规则使用绝对不会重复的日期,所以不会遇到第一个红框中合并计算的问题。

累计函数还有 avg() min() 等等,这些都一样可以作用于窗口函数,其逻辑可以按照下图理解:

带你快速了解SQL窗口函数

你可能有疑问,直接 sum(上一行结果,下一行) 不是更方便吗?为了验证猜想,我们试试 avg() 的结果:

带你快速了解SQL窗口函数

可见,如果直接利用上一行结果的缓存,那么 avg 结果必然是不准确的,所以窗口累计聚合是每行重新计算的。当然也不排除对于 sum、max、min 做额外性能优化的可能性,但 avg 只能每行重头计算。

与 GROUP BY 组合使用

窗口函数是可以与 GROUP BY 组合使用的,遵循的规则是,窗口范围对后面的查询结果生效,所以其实并不关心是否进行了 GROUP BY。我们看下面的例子:

带你快速了解SQL窗口函数

按照地区分组后进行累加聚合,是对 GROUP BY 后的数据行粒度进行的,而不是之前的明细行。

来源:https://juejin.cn/post/7079969705540681764

标签:sql,窗口函数
0
投稿

猜你喜欢

  • python连接mysql并提交mysql事务示例

    2024-01-15 04:43:37
  • Python脚本提取fasta文件单序列信息实现

    2023-03-06 12:55:02
  • python 如何将office文件转换为PDF

    2022-10-07 11:41:48
  • MySQL Packet for query is too large 问题及解决方法

    2024-01-29 07:55:52
  • golang中如何保证精度的方法

    2024-04-26 17:23:22
  • 详解Python的基础语法和变量操作

    2021-10-13 17:07:54
  • pytest自动化测试fixture的作用域实例化顺序及可用性

    2022-06-22 14:46:30
  • 浅谈Pandas 排序之后索引的问题

    2022-03-18 12:28:32
  • MYSQL实现添加购物车时防止重复添加示例代码

    2024-01-15 01:44:15
  • sqlserver存储过程语法详解

    2024-01-21 04:43:53
  • Web2.0视觉风格进化论 之二

    2007-11-03 20:10:00
  • Python SSL证书验证问题解决方案

    2022-11-06 13:54:35
  • 使用python计算方差方式——pandas.series.std()

    2022-05-24 14:03:35
  • 学习SQL语句(强大的group by与select from模式)

    2011-11-03 16:46:09
  • php引用和拷贝的区别知识点总结

    2023-11-15 03:39:48
  • python中defaultdict用法实例详解

    2022-08-09 17:01:10
  • C#操作本地文件及保存文件到数据库的基本方法总结

    2024-01-17 10:27:55
  • js获取 type=radio 值的方法

    2024-04-29 13:18:38
  • python小程序基于Jupyter实现天气查询的方法

    2021-05-30 10:39:40
  • Python pyecharts数据可视化实例详解

    2023-06-28 01:35:30
  • asp之家 网络编程 m.aspxhome.com