窗口函数的概念

发布于:2025-06-25 ⋅ 阅读:(20) ⋅ 点赞:(0)

SQL的世界中,窗口函数(也称为开窗函数)可以算是一把高效数据分析的利器。对于数据工程师或者相关人员来说,掌握窗口函数是迈向高效处理数据的重要一步。举个例子:现数据库中有如下一张销量表数据,展示了一年中每个月份的销售额:

图片
如何快速用SQL求出每个月的销量排名、季度累计销量、年度累计销量、同比和环比增长率等?这就得靠咱们今天的主角:窗口函数。

一、概念

窗口函数是在 SQL 查询中对一组相关联的记录进行计算而返回一个值的函数。与普通的聚合函数(如SUM, AVG等)类似,但有一个关键区别:窗口函数不会将多行合并成单行输出,而是保留原始的行数,并在每行上附加计算结果。这意味着你可以在同一行中同时看到原始数据和聚合结果。

窗口函数的语法结构通常为:

函数名() OVER ( [partition by] order by() )
其中,能够作为窗口函数的有,如 SUM、AVG、COUNT、MIN、MAX 这样的聚合函数,还有RANK、DENSE_RANK、ROW_NUMBER这样的专用窗口函数;OVER 子句定义了窗口的范围和排序规则,它的内部有partition by和order by。“partition by” 将数据集划分成多个分区,每个分区独立计算,其可以省略,即不划区域,针对整个数据集;order by则用于指定分区内的排序顺序。这样解释可能有点抽象,我们一起来看具体的例子。

二、排序窗口函数

能作为窗口函数使用的有很多,先从简单的排序函数开始看起,其主要用于对数据赋予相应的序号(或者说排名值),特别是在需要对数据进行分层分组、组内排序和比较的场景中。常见的排序窗口函数有3个:ROW_NUMBER()、RANK()和DENSE_RANK()。

1.ROW_NUMBER()

ROW_NUMBER() 函数为分区内的每一行赋予一个唯一的连续整数编号。这个编号从 1 开始,按照指定的排序规则依次递增。

例如,在商品销售数据表中,我们可以使用如下SQL为商品按照销售量从高到低进行排名编号:

SELECT sales_date,
quarter,
sales_volume,
– 销量排名(按销量降序)
ROW_NUMBER() OVER (ORDER BY sales_volume DESC) AS sales_rank
FROM dbo.test;
结果如下:

图片
得到了一年中12个月的销量排名,如果想还是按原来的1-12月进行排序,可以在最后再加一个ORDER BY sales_date。看到上面的SQL中是没有partition by的,即没有进行分区,针对的是所有时间段的数据,如果我们想按季度进行分区(对应的是组内排序场景),获取到每个季度中各月的排名,可以写成:

SELECT sales_date,
quarter,
sales_volume,
– 销量排名(按销量降序)
ROW_NUMBER() OVER (ORDER BY sales_volume DESC) AS sales_rank,
– 销量季度排名
ROW_NUMBER() OVER (PARTITION BY quarter ORDER BY sales_volume DESC) AS quarter_rank
FROM dbo.test
ORDER BY sales_date
图片
即使有销量数据相同的情况,ROW_NUMBER() 也会为它们分配不同的序号,确保每个排名值的唯一性。

2.RANK()

RANK() 函数与 ROW_NUMBER() 类似,也是用于对数据进行排名。但是,RANK() 函数在遇到相同值的行时,会赋予它们相同的排名值,并且后续行的排名会跳跃。简单记忆就是在班级中的成绩排名,如果有两个人100分,他们并列第一,后面的99分是第3名。

用法和上面的ROW_NUMBER()差不多,例如:

SELECT sales_date,
quarter,
sales_volume,
– 销量排名(按销量降序)
RANK() OVER (ORDER BY sales_volume DESC) AS sales_rank
FROM dbo.test
图片
大家可以看到2024年2月和3月的数据相同时,rank()的结果都是8,而后的是10。这种排名方式常用在一些需要突出显示相同排名数据的场景中,比如在比赛成绩排名中,若多个选手得分相同,则他们共享同一个名次,而后续选手的名次则相应顺延。

3.DENSE_RANK()

还有第3个排名函数DENSE_RANK() ,与 RANK() 不同的是,DENSE_RANK() 在遇到相同值时,赋予它们相同的排名,但后续行的排名不会跳跃。也就是说,排名值是连续的。例如:

图片

对于这3种排序函数,用法上相似,只是针对相同值时生成的序号有所不同,大家根据实际的情况选择合适的即可。

本来还准备写关于作为窗口函数使用的聚合函数,由于篇幅限制,所以另写一篇吧。这里给大家做个总结:

窗口函数可以类比前面学的聚合函数,但是光靠聚合函数,有时候不好解决问题。因为聚合函数的特定是“汇总”统计,本来有十几行数据,sum一下,只得到了一个总的值,将原本N行数据进行了“压缩”,这种聚合有时会“丢失”细节信息。所以后来为了方便处理一些数据分析的情况,就有了窗口函数(MySQL是8.0版本开始才支持的窗口函数),也被称为OLAP函数(OnLine Analytical Processing)。

它有两个关键字partition by和order by:partition by是分区、分组,能够为我们要排序、或者求和、统计的对象划定一个区域(窗口),partition by可以没有,即不划定小的区域,针对所有的数据。order by是指定按照哪一列、何种顺序进行排序,是对销量还是什么指标进行排序,是升序还是降序。关于排名的窗口函数:ROW_NUMBER()、RANK()、DENSE_RANK(),首先需要记住它们三的区别,即针对相同数据的情况是否会“跳号”,然后就是它们的用法。

下篇会继续介绍窗口函数的其它知识和实际案例,希望这篇文章对你有帮助。如果你在学习或工作中遇到相关问题,欢迎随时交流探讨!

—END—


网站公告

今日签到

点亮在社区的每一天
去签到