今天想重提窗口函数。原因是前几天在群里提起了这个名字,忘了是什么缘由提起的,但令我吃惊的是,竟还有同学想从事数据分析却不知道窗口函数!那感觉就仿佛用勺子吃面条不知道有筷子这种好东西一样。

他问:“有没有大佬简单说下窗口函数是个什么东西?”

胖里:不减少原表行数情况下,对数据进行分组排序。

阿鑫:在SQL中窗口函数可以对数据进行同步处理,where和group by处理后进行操作,只能写在select子句中。

其他:百度吧,知乎看一下。

然后我去翻了翻公众号之前关于窗口函数的文章,我以为自己对这部分内容已经了解和解释的挺清楚了,无论是用法还是实例。但我翻完才发现,好像并没有对窗口函数下定义,其他人的很多文章也大都没对窗口函数的定义进行描述,都从窗口函数有什么用,怎么用,举例子开始。

窗口函数到底是什么呢?

01

窗口函数是什么

我拿这个问题去问交流群里的小伙伴,得到了如下一些回答。

A:处理分析的函数,类似于聚合函数?

B:用于解决组内排序、聚合等运算且只能写在select字句中的函数。

C:窗口函数可以切分小组,并在小组内实现排序、聚合等数据处理操作。

D:窗口函数的含义就像它的名字,开一个窗子,在不影响房屋原来结构的基础上从自己想要的角度观察内部关系。

E:窗口函数类似于聚合函数。区别是聚合函数对每组只返回一个值,窗口函数返回多个值,也就是说对组里的每条记录都会产生返回值~

每个人都讲出了自己的理解,每个人理解的也都没什么问题。在我看来,窗口函数无非就是一种略高级的操作,能划分范围(组),对这一范围内的数据进行某种处理,可以是聚合,可以是排序、也可以是求第一个记录、最后一个记录等。它有其高级之处,也有用法上的某些限制。

窗口函数,也称为OLAP(Online Analytical Processing)函数,之所以叫窗口函数是为了便于形成直观印象,易于理解(虽然可能对一些同学而言,并没有那么容易理解。)

如上便是窗口函数的定义,要想理解窗口函数从定义上是远远不够的,最好能从窗口函数的语法及其实例来理解。

上图便是窗口函数的语法了,你可以将其看作一种固定搭配,填充进去不同的窗口函数、指定列,即可实现对应函数能实现的操作。

这其中最重要的关键字便是partition byorder bypartition by用来圈定所要进行操作的对象的范围,order by用来指定按照哪列、何种顺序进行排序。通过partition by分组后的记录集合叫做窗口,此处的窗口表示范围,这也是窗口函数名字的由来。

但partition by并非必需,不使用partition by也可正常使用窗口函数,只不过此时等于不分组,将整张表作为一个大窗口而已。

02

窗口函数的分类

说完窗口函数的语法,不妨来看看常用/常见的一些窗口函数及其分类。

下图中罗列了三种窗口函数的分类,其实分类这种东西,都是主观上按照某些客观规则划分的,划分的人不一样也就形成了不同或不同粒度的规则。

窗口函数可归为两大类,聚合类窗口函数和非聚合类窗口函数(也可称为专用窗口函数)。聚合类窗口函数是将我们常用的聚合函数作为窗口函数使用,非聚合类窗口函数指一些规定好的窗口函数,按照其不同的功能进行定义和划分,如常见的排序函数、分布函数和偏移函数。

注意,有的窗口函数是带参数的,有的不带参数,如sum(amt)、lead(time)、rank()、row_number()。

其实我们可能并不需要知道所有的窗口函数,我这水平目前也只知道聚合、排序、返回值的几个函数,一些是因为面试常考他们的相同和不同点,一些是业务或笔试题中会用得到。这些函数基本懂一部分就够用了,其他需要的可以根据需求再进行查询、学习和使用。

03

窗口函数的应用

笼统地介绍完一些常见的窗口函数,还是好好说说其中一些函数的用法吧。

先来几个简单的示例,初步了解下窗口函数的使用。

下表为某公司在各地区不同月份的销售额记录表,sales_table(虚构数据)。

题目1:求取每个地区的销售额(以表内所示月份为例)

select area,sum(sale_amt) -- 每地区总销售额
from sales_table
group by area;

这个题目算是在学习SQL基础的时候比较简单且常见的题目了吧,毫无难度的那种,求某个分组的和,group by,sum()一下就OK了。

题目2:求每个地区每月销售额占比,以及月累积销售额占比

按之前分组求sum(),只能得到每个地区对应的总销售额,分别为1500000、870000、1640000,而每个地区每月销售额占比就得实现500000/1500000,450000/1500000等操作了,此结果也可通过先求每地区的总销售额然后表关联进行字段间的除法操作,不过有了窗口函数,一切便变得简单了些。

select *,sale_amt/sum_amt as sale_amt_ratio -- 每地区每月销售额占比
from(select *,sum(sale_amt) over(partition by area order by area) as sum_amt from sales_table
)as t

用sum()聚合函数作为窗口函数,使用partition by area将地区作为分组,在地区内求得每个地区的总销售额,此处order by由于语法原因,不可缺,但由于分组后的地区只有自己本身这个地区,所以此处的order by无实际意义。

结果如下所示,求得了每个地区每月销售额占比:

那累积销售额占比呢?还是可以用sum()聚合函数做窗口函数,分组依旧是按照地区进行分组,但累积销售额就牵扯到时间先后了,因此要学会善用order by对日期进行排序,才能求得真正时间顺序上的累积销售额。

select *,agg_amt/sum_amt as agg_sale_amt_ratio -- 每地区月累积销售额占比
from(select *,sum(sale_amt) over(partition by area order by sale_time) as agg_amt -- 每地区月累积销售额,sum(sale_amt) over(partition by area order by area) as sum_amt -- 每地区总销售额from sales_table
)as t

经过上述这番操作,应该初步认识到窗口函数所谓的高级及实用之处了吧,无非就是将原先需要各种分别求取再join的操作,通过窗口函数的应用在多个子查询中实现。上述的两个sum()窗口函数能同时求取,放在一个子查询内,也就是说一个select子句中可有多个窗口函数。

注意:求累积的时候不仅可以实现上述的按月累积,也可实现限定的按前/后/前后几个月累积,毕竟有时候有些需求是从头到尾累积,而有的是要求前后共三个月的累积。

这时候用来在窗口中指定更加详细汇总范围的功能便出现了,此功能中的汇总范围叫做框架,使用方法就是在order by子句后使用用来指定范围的关键字following和preceding。

举个小例子。

select *,sum(sale_amt) over(partition by area order by sale_time rows between 1 preceding and 1 following) as agg_amt -- 当前行及其前一行和后一行
from sales_table;select *,sum(sale_amt) over(partition by area order by sale_time rows 1 preceding) as agg_amt -- 当前行及其前一行
from sales_table;-- mysql单独运行following时报错,未解,但可使用rows between 0 preceding and 1 following来替代
select *,sum(sale_amt) over(partition by area order by sale_time rows 1 following) as agg_amt -- 当前行及其后一行
from sales_table;

上述例子即为指定更详细的汇总范围了,preceding指前多少行,following指后多少行,然后再加上当前的行,如rows between 1 preceding and 1 following 表示前一行、后一行加当前行,共三行。

题目3:求每个地区各个月销售额排名情况

说到排名,应该能想到排序吧,如果没接触过窗口函数想要比较大小,你们都会用什么方法呢?对于排序,我首先想到的就是order by了,order by不就是用来排序的吗?能实现顺序排列,但只是不打标,记录数太多可能就不知道有多少名了,除此之外我满脑子都是各种表的关联、对比。

不想那么复杂的话,就试试窗口函数中用于排序的函数吧。说到排序的窗口函数,不少人应该都知道rank(),dense_rank(),row_number(),除了在笔试题中考到用他们来排序,面试也常问这三者的区别是什么。

select *,row_number() over(partition by area order by sale_amt desc) as rnk
from sales_table;

使用窗口函数,轻松实现按地区分组,对每个月的销售额进行排序打标,一目了然,不仅可以排序,设想一下这种情况,你们公司的数据库表里不仅只有这10条数据,而是有成百上千甚至上万条数据,老板让你看看每个地区销售额最高的2个月份是哪两个,你总不至于分别求取吧,一个排序窗口函数就能解决的问题,何乐而不为?

此处我只用了我最常用的row_number()来实现,针对不同的排序要求,可选择不一样的排序函数,这三种排序函数不太清楚的自己回去反思吧。

有一张表log_table,其中包括用户id(user_id)及访问时间(visit_time)两个字段。

题目4:每天随机取1000个用户

select user_id,visit_time
from
(select user_id,visit_time,row_number() over(partition by visit_time order by rand()) as rnkfrom log_table
)as t
where rnk <= 1000;

当然,这个题目,不使用窗口函数依旧也可以通过order by rand()实现。

题目5:每天随机取10%个用户

select user_id,visit_time
from
(select user_id,visit_time,percent_rank() over(partition by visit_time order by rand()) as rnk_ratiofrom log_table
)as t
where rnk_ratio <= 0.1;

当然,这个题目也可以不用窗口函数,通过计算用户总数,再随机取用户总数的10%。

不尽兴的话,可以再来一张用户表user_table,里面存储了某平台某天的活跃用户user_id,且不重复。

题目6:将用户随机分成10组,每组取1000个用户。

select user_id
from
(select user_id,n_rnk,row_number() over(partition by n_rnk order by rand()) as rnkfrom (select user_id ,ntile(10) over(order by rand()) as n_rnk from user_table)as t1
)as t2
where rnk <= 1000;

随机分十组,用窗口函数的ntile()可以实现分桶(分组功能),再使用一次排序,随机从每组中选出1000个用户。

题目7:将用户随机分成100组,每组取10%个用户

这个题目的答案就显而易见了吧。

上述几个简单的示例是为了说明窗口函数的用法及其意义所在,使用窗口函数可能会使解决问题的过程变得更简单,对窗口函数的熟悉和理解可以帮助我们在之后遇到问题的时候,虽然是取数,但可以更有针对性、更高效地取数。

除上述一些简单示例外,笔面试中常考的需要用窗口函数解决的问题一般包括topn问题、连续登录问题。这两种题目面试中问到的频率特别高,前几天群里有个小伙伴就说被问到了,但一时想不出如何解。

想来还是对窗口函数不熟悉吧,哪一类的问题涉及到什么关键字,应该用哪种函数解决,这些虽然在各种文章中频被提及,但你如何理解,是否真正理解且能应用,就是个人的问题了。

●分析了 40000+ 条内衣数据,我终于发现了罩杯的秘密...

后台回复“入群”即可加入小z数据干货交流群

详细解读 SQL 窗口函数相关推荐

  1. 详细解读SQL Server 函数

    一旦成功地从表中检索出数据,就需要进一步操纵这些数据,以获得有用或有意义的结果.这些要求包括:执行计算与数学运算.转换数据.解析数值.组合值和聚合一个范围内的值等. 下表给出了T-SQL函数的类别和描 ...

  2. python 窗口函数_太好用了!Python中8种流行的SQL窗口函数了解一下!

    有关如何在业务分析中利用Pandas来有效复制最常用的SQL窗口函数的教程. 任何致力于构建公司KPI或创建分析随着时间推移的业务绩效的数据分析人员都知道SQL窗口功能是一个强大的工具.使用它们的真正 ...

  3. 详细解读阿里云开源PolarDB总体架构和企业级特性

    在3月2日的阿里云开源 PolarDB 企业级架构发布会上,阿里云 PolarDB 内核技术专家 北侠带来了主题为<PolarDB 总体架构设计和企业级特性>的精彩演讲.主要分享了存储计算 ...

  4. Spring Boot 集成SnakerFlow流程引擎,简介、功能列表、详细解读、扩展点分析

    文章目录 简介 功能列表 流程定义 任务参与者 参与者设置 动态添加.删除参与者 组支持 详细解读 Spring Boot集成 表定义 表详细说明: 字段详细说明: 常见操作 常规API 综合查询 模 ...

  5. openGauss WDR Snapshot生成性能报告详细解读

    openGauss WDR Snapshot生成性能报告详细解读 1. 简介 2. 操作步骤 2.1. 参数启用 2.1.1 启用WDR报告的snapshot收集 2.1.2 启用资源实时监控功能 2 ...

  6. AFL(American Fuzzy Lop)源码详细解读(3)

    AFL(American Fuzzy Lop)源码详细解读(3) 本篇是关于主循环阶段的内容,整个AFL最核心的部分,篇幅较长.最后简述一下afl_fuzz整体流程. 多亏大佬们的文章,对读源码帮助很 ...

  7. 等保测评2.0超详细解读,收藏这一篇就够了

    一.等级保护介绍 1.1什么是等级保护 网络安全等级保护是指对国家重要信息.法人和其他组织及公民的专有信息以及信息和存储.传输.处理这些信息的信息系统分等级实行安全保护,对信息系统中使用的信息安全产品 ...

  8. mysql 阿里_详细解读阿里手册之MySQL

    阿里手册是阿里工程师多年一线经验的结晶,遵循其中的约定与规范,能很大程度的减少某些未知的隐患. 其规约强度由强到弱分为强制.推荐.参考三大类. 本文详细解读了阿里手册的MySQL部分,如果是一些很明确 ...

  9. NLP突破性成果 BERT 模型详细解读 bert参数微调

    https://zhuanlan.zhihu.com/p/46997268 NLP突破性成果 BERT 模型详细解读 章鱼小丸子 不懂算法的产品经理不是好的程序员 ​关注她 82 人赞了该文章 Goo ...

  10. VINS-mono详细解读与实现

    VINS-mono详细解读 VINS-mono详细解读 前言 Vins-mono是香港科技大学开源的一个VIO算法,https://github.com/HKUST-Aerial-Robotics/V ...

最新文章

  1. java gui 选项_【Java-GUI】04 菜单
  2. mysql权限配置文件_MySQL权限管理、配置文件(三)
  3. C++_向函数传递对象
  4. Session与Cookie
  5. BugkuCTF-Misc:做个游戏(08067CTF)
  6. 我的Thinkpad T410最近老是出Fan error
  7. PHP调试环境搭建的一些常见问题
  8. python中forward函数的引用_pytorch 调用forward 的具体流程
  9. 使用delphi 开发多层应用(二十二)使用kbmMW 的认证管理器
  10. C#开发微信公众平台-就这么简单(附Demo)
  11. 软件测试的基础知识(二)
  12. 关于使用stanfordcorenlp一直运行不报错的解决方法
  13. 【OpenCV入门指南】第三篇Canny边缘检测
  14. Ubuntu -- 无法正常安装卸载ssh以及chattr无反应的问题
  15. python爬取谷歌学术_python爬取谷歌热度
  16. 镜像站(整理各个镜像站资源)
  17. 电源滤波为何通常是一大一小两个电容并联?
  18. android storyboard,iOS中xib与storyboard原理,与Android界面布局的异同
  19. 学完可入狱:《Web渗透测试-基于Kali Linux》经典黑客入门教程
  20. 云洲智能在科创板恢复IPO审核,拟募集资金15.5亿元

热门文章

  1. C#简单实现office转pdf、pdf转图片
  2. C# 开发的 webBrowser打开网页出现脚本错误解决
  3. Fluent Python读后感
  4. 基于STM32F429,Cubemx的SDHC卡的基本Fatfs文件移植
  5. EA建模-绘制活动图
  6. linux dx游戏,10大免费 Linux 游戏
  7. 计算机科学与工程学院金巍,附件1湖北省思想政教育先进高校名单-附件1-.doc
  8. 2018-2019年江苏省高等学校“阿里云大数据技术实战训练营”大学生万人计划学术冬令营开营... 1
  9. 135微信编辑html语言,135微信编辑器
  10. 第十一章 卡米洛特的黑暗时代