最近在牛客解sql题目时,经常有一类题目要求对表中的某一列进行排序
在查看提供的题解时经常看到使用开窗函数的解决方案
虽然不用开窗函数也能通过子查询的方式获取正确答案,但是
比较复杂难以理解,
所以很有必要学习
第1行表示id为1的用户通过了4个题目;
…
第6行表示id为6的用户通过了4个题目;
思考:
题目只有一张表,两个字段,看似很简单,
如果没有t_rank要组合的话只需要
但我们现在需要组合出t_rank,
在不使用开窗函数情况下的解题思路:
我开始时想通过聚合函数构造一个字段,但事情并不简单,需要得出排名并非能够通过一个聚合解决
需要使用通过子查询来得出排名的值
具体实现(参考该题的第一名的解答):
要得到通过题目的排名,比如你通过了8分,,你同学a也通过了8分,找到大于等于你的成绩,一个9分,一个8分,一个8分,去重复,就一个9,一个8,count一下总数,第2名,如果有三个同学通过了7个呢,同理,9,8,8,7,7,7 后面比这个少的,已经死在了筛选条件,去重,9,8,7,count=3,
确实,如果我们得了10分,没有人超过我们,那么找到大于等于我的成绩,及10,count一下就是1了,即第一名,
故总结为
如此,加上最初的那句简单sql,即可以得出本题的一种答案
显然这个逻辑还是有一点复杂的,
下面先显示使用开窗函数的答案
这个答案也可以达到一样的效果
这个sql语句则简洁很多,现在需要理解的就只是这个开窗函数了
前言:老版本的mysql并不支持开窗函数,如需使用请使用mysql8以上的版本
开窗函数的定义:
开窗函数与聚合函数:
开窗函数的使用
从开窗函数的定义中,可以了解到开窗函数比普通的聚合函数更加的灵活,功能也更加强大。但并不是很好理解,那么先从它的使用开始吧
开窗函数的一个概念是当前行,当前行属于某个窗口,窗口由
over关键字用来指定函数执行的窗口范围,如果后面括号中什么都不写,则意味着窗口包含满足where条件的所有行,开窗函数基于所有行进行计算;如果不为空,则有三个参数来设置窗口:
partition by子句:
按照指定字段进行分区,两个分区由边界分隔,开窗函数在不同的分区内分别执行,在跨越分区边界时重新初始化。
order by子句:
按照指定字段进行排序,开窗函数将按照排序后的记录顺序进行编号。可以和partition by子句配合使用,也可以单独使用。
frame子句:
当前分区的一个子集,用来定义子集的规则,通常用来作为滑动窗口使用。
常用的开窗函数:
这里直接在官网查了一下,网址如下MySQL :: MySQL 8.0 Reference Manual :: 12.21.1 Window Function Descriptions
以上大部分函数的使用效果用例都可以在上面的官网中看到示例,
下面是在实际中的使用:
如上面的原题,
分析其开窗函数的含义:
突然发现了新东西貌似比较有趣,明天接着更
构造一张表,在其中插入一些信息
我们先利用几个开窗函数中的排名函数来测试一下:
再测试一下计算累计分布值,和Bucket number的开窗函数
我先查一下bucket number,发现网上并没有这个东西的准确信息,只能在官网里看其定义了
以上表的数据测试一下:
Window关键字的用法可以参考
MySQL8中的关键字window与窗口函数_程序员先森的博客-CSDN博客
测试:
通过partition by 关键字确定开窗函数的分区,
例:
官网的介绍:MySQL :: MySQL 8.0 Reference Manual :: 12.21.3 Window Function Frame Specification
与窗口函数一起使用的窗口的定义可以包括Frame子句。 Frame是当前分区的子集,Frame子句指定如何定义子集。
Frame是相对于当前行确定的,这使得Frame能够根据当前行在其分区内的位置在分区内移动。
例子: 通过将一个框架定义为从分区开始到当前行的所有行,您可以计算每行的运行总数。
通过将框架定义为在当前行的任一侧扩展 N 行,您可以计算滚动平均值。
官网的介绍很复杂,这里有一篇更优秀的文章可以参考