MySQL之窗口函数

MySQL 窗口函数


一天,求职者小A接到面试电话,如约前往面试。

在面试中,面试官问:问题A,现在假设有如下一张表存储了考生的考试成绩,现在需要张榜公示考试排名成绩。请问你该如何写这个SQL?


CREATE TABLE `t_stud` (

`studid` int DEFAULT NULL COMMENT '学生ID',

`classid` tinyint DEFAULT NULL COMMENT '班级ID',

`score` int DEFAULT NULL COMMENT '得分'

)


表中的数据为如下:


mysql> select * from t_stud;

+--------+---------+-------+

| studid | classid | score |

+--------+---------+-------+

| 1001 | 1 | 98 |

| 1002 | 1 | 99 |

| 1003 | 1 | 100 |

| 1004 | 1 | 89 |

| 1005 | 1 | 89 |

| 1006 | 1 | 89 |

| 1007 | 2 | 99 |

| 1008 | 2 | 34 |

| 1009 | 2 | 56 |

| 1010 | 2 | 99 |

| 1011 | 2 | 90 |

| 1012 | 2 | 56 |

| 1013 | 2 | 56 |

+--------+---------+-------+

13 rows in set (0.00 sec)


小A暗自窃喜,幸亏昨晚临时看了一下前辈们总结的面试SQL必问秘籍,里面就有一个类似的SQL解答。于是稍微整理了一下思路,提笔作答如下:


select @row_id :=@row_id+1 as row_id,studid,classid,score

from ( select studid,classid,score,@row_id:=0

from t_stud

order by score desc ) as tmp;


返回结果如下:

+--------+--------+---------+-------+

| row_id | studid | classid | score |

+--------+--------+---------+-------+

| 1 | 1003 | 1 | 100 |

| 2 | 1002 | 1 | 99 |

| 3 | 1007 | 2 | 99 |

| 4 | 1010 | 2 | 99 |

| 5 | 1001 | 1 | 98 |

| 6 | 1011 | 2 | 90 |

| 7 | 1004 | 1 | 89 |

| 8 | 1005 | 1 | 89 |

| 9 | 1006 | 1 | 89 |

| 10 | 1009 | 2 | 56 |

| 11 | 1012 | 2 | 56 |

| 12 | 1013 | 2 | 56 |

| 13 | 1008 | 2 | 34 |

+--------+--------+---------+-------+


面试官接着问:问题B,如果按照班级,进行班级内部排名,又该如何写SQL呢?


小A,略微思考了一下,接着提笔作答如下:


select @row_id :=case when @classid=classid

Then @row_id+1

Else

end as row_id

,@classid :=classid

,studid

,score

from ( select studid

,classid

,score

,@row_id:=0

,@classid=0

from t_stud

order by classid,score desc ) as tmp;


返回结果如下:

+--------+--------------------+--------+-------+

| row_id | @classid :=classid | studid | score |

+--------+--------------------+--------+-------+

| 1 | 1 | 1003 | 100 |

| 2 | 1 | 1002 | 99 |

| 3 | 1 | 1001 | 98 |

| 4 | 1 | 1004 | 89 |

| 5 | 1 | 1005 | 89 |

| 6 | 1 | 1006 | 89 |

| 1 | 2 | 1007 | 99 |

| 2 | 2 | 1010 | 99 |

| 3 | 2 | 1011 | 90 |

| 4 | 2 | 1009 | 56 |

| 5 | 2 | 1012 | 56 |

| 6 | 2 | 1013 | 56 |

| 7 | 2 | 1008 | 34 |

+--------+--------------------+--------+-------+


面试官接着问:你上面的解决方法都是8.0版本之前的解决方法,能否用8.0版本提供的方法更加简单地解决以上问题呢?


小A:我目前项目都是基于5.6,5.7版本的,目前还未接触8.0版本,所以,还不是很熟悉,面试官,能否请您给我介绍一下8.0版本提供的新方法呢?我可以学习一下。谢谢!


面试官:好的,那我就简单地介绍一下吧。MySQL从8.0版本开始支持窗口函数,窗口函数总体上我们可以分为序号函数, 分布函数, 前后函数, 首尾函数和其他函数


窗口函数的完整语法是:


window_spec:

[window_name] [partition_clause] [order_clause] [frame_clause]


partition_clause:

PARTITION BY expr [, expr] ...


order_clause:

ORDER BY expr [ASC|DESC] [, expr [ASC|DESC]] ...


frame_clause:

frame_units frame_extent

frame_units:

{ROWS | RANGE}


frame_extent:

{frame_start | frame_between}

frame_between:

BETWEEN frame_start AND frame_end

frame_start, frame_end: {

CURRENT ROW

| UNBOUNDED PRECEDING

| UNBOUNDED FOLLOWING

| expr PRECEDING

| expr FOLLOWING

}


在了解和学习窗口之前,让我们先检查一下服务器的版本:


mysql> select version();

+-----------+

| version() |

+-----------+

| 8.0.28 |

+-----------+


可以看到,目前服务器的版本为:8.0.28

接下来,我们便具体谈谈每一个窗口函数的具体使用场景和使用方法。


一:序号函数:

序号函数,主要包含如下三个窗口函数

1. Row_number():顺序排名函数,也就是上面两道题目的8.0的解决方法,其特点是排名具有连续性,即使依据排名的对应列等值。让我们来看看上面两道面试题目的在8.0版本中的对应解决方法。

对于问题A


select row_number() over (order by score desc) as row_id,studid,classid,score

from t_stud ;


返回结果如下:


+------------+------------+-------------+-----------+

| row_id | studid | classid | score |

+------------+------------+--------------+-----------+

| 1 | 1003 | 1 | 100 |

| 2 | 1002 | 1 | 99 |

| 3 | 1007 | 2 | 99 |

| 4 | 1010 | 2 | 99 |

| 5 | 1001 | 1 | 98 |

| 6 | 1011 | 2 | 90 |

| 7 | 1004 | 1 | 89 |

| 8 | 1005 | 1 | 89 |

| 9 | 1006 | 1 | 89 |

| 10 | 1009 | 2 | 56 |

| 11 | 1012 | 2 | 56 |

| 12 | 1013 | 2 | 56 |

| 13 | 1008 | 2 | 34 |

+------------+-------------+-------------+-----------+

13 rows in set (0.04 sec)


对应问题B


select row_number() over (partition by classid order by score desc) as row_id,studid,classid,score

from t_stud ;


返回结果如下:


+------------+------------+-------------+-----------+

| row_id | studid | classid | score |

+------------+------------+-------------+-----------+

| 1 | 1003 | 1 | 100 |

| 2 | 1002 | 1 | 99 |

| 3 | 1001 | 1 | 98 |

| 4 | 1004 | 1 | 89 |

| 5 | 1005 | 1 | 89 |

| 6 | 1006 | 1 | 89 |

| 1 | 1007 | 2 | 99 |

| 2 | 1010 | 2 | 99 |

| 3 | 1011 | 2 | 90 |

| 4 | 1009 | 2 | 56 |

| 5 | 1012 | 2 | 56 |

| 6 | 1013 | 2 | 56 |

| 7 | 1008 | 2 | 34 |

+------------+------------+-------------+-----------+

13 rows in set (0.00 sec)



从上面SQL的写法上看,窗口函数比以前借助变量来实现的方法更加简洁,清晰。


2. rank():并列排名函数,重复名次的将会被跳过,输入结果类似与1,1,3,......

我们依旧用上面的表数据做例子,现在新的需求是:当得分相同时,排名相同,也就是我们经常说的并列第N名,但是后续的排名因为并列名次的原因需要被跳过,并列多少名就跳过多少名,然后再从跳过的顺序名次开始。


select rank() over (order by score desc) as row_id,studid,classid,score

from t_stud ;


返回结果如下:


+------------+-------------+-------------+-----------+

| row_id | studid | classid | score |

+------------+------------+-------------+------------+

| 1 | 1003 | 1 | 100 |

| 2 | 1002 | 1 | 99 |

| 2 | 1007 | 2 | 99 |

| 2 | 1010 | 2 | 99 |

| 5 | 1001 | 1 | 98 |

| 6 | 1011 | 2 | 90 |

| 7 | 1004 | 1 | 89 |

| 7 | 1005 | 1 | 89 |

| 7 | 1006 | 1 | 89 |

| 10 | 1009 | 2 | 56 |

| 10 | 1012 | 2 | 56 |

| 10 | 1013 | 2 | 56 |

| 13 | 1008 | 2 | 34 |

+------------+-------------+-------------+-----------+

13 rows in set (0.01 sec)


3. dense_rank():并列排名函数,重复名次的将不会被跳过,输入结果类似与1,1,2,......

这次我们的要求更改为:当得分相同时,排名相同,也就是我们经常说的并列第N名,但是后续的排名需要接着继续开始排名,就是排名不能中断。


select dense_rank() over (order by score desc) as row_id,studid,classid,score

from t_stud ;


返回结果如下:

+------------+------------+-------------+-----------+

| row_id | studid | classid | score |

+------------+------------+-------------+-----------+

| 1 | 1003 | 1 | 100 |

| 2 | 1002 | 1 | 99 |

| 2 | 1007 | 2 | 99 |

| 2 | 1010 | 2 | 99 |

| 3 | 1001 | 1 | 98 |

| 4 | 1011 | 2 | 90 |

| 5 | 1004 | 1 | 89 |

| 5 | 1005 | 1 | 89 |

| 5 | 1006 | 1 | 89 |

| 6 | 1009 | 2 | 56 |

| 6 | 1012 | 2 | 56 |

| 6 | 1013 | 2 | 56 |

| 7 | 1008 | 2 | 34 |

+------------+------------+---------------+-----------+

13 rows in set (0.00 sec)


现在让我们把上面的三个SQL合在一起:


Select row_number() over (order by score desc) as row_number_id

, rank() over (order by score desc) as rank_id

, dense_rank() over (order by score desc) as dense_rank_id

, studid

, classid

, score

from t_stud ;


此时,我们看到,Over语句都是相同的,为了使得代码再进一步简洁,我们使用窗口函数的标准语法来进行改写,改写如下:


Select row_number() over w as row_number_id

, rank() over w as rank_id

, dense_rank() over w as dense_rank_id

, studid

, classid

, score

from t_stud

window w as (order by score desc) ;


可以看到,这里window_namew , order_clause order by score desc

返回结果如下:


+------------------------+-------------+------------------------+------------+-------------+-----------+

| row_number_id | rank_id | dense_rank_id | studid | classid | score |

+------------------------+-------------+------------------------+------------+--------------+----------+

| 1 | 1 | 1 | 1003 | 1 | 100 |

| 2 | 2 | 2 | 1002 | 1 | 99 |

| 3 | 2 | 2 | 1007 | 2 | 99 |

| 4 | 2 | 2 | 1010 | 2 | 99 |

| 5 | 5 | 3 | 1001 | 1 | 98 |

| 6 | 6 | 4 | 1011 | 2 | 90 |

| 7 | 7 | 5 | 1004 | 1 | 89 |

| 8 | 7 | 5 | 1005 | 1 | 89 |

| 9 | 7 | 5 | 1006 | 1 | 89 |

| 10 | 10 | 6 | 1009 | 2 | 56 |

| 11 | 10 | 6 | 1012 | 2 | 56 |

| 12 | 10 | 6 | 1013 | 2 | 56 |

| 13 | 13 | 7 | 1008 | 2 | 34 |

+------------------------+--------------+-----------------------+------------+--------------+----------+

13 rows in set (0.00 sec)


二:分布函数

分布函数,主要有如下两个窗口函数:

1. Cume_dist():累计分布值,值 = 小于或等于当前行值的行数 / 总行数,适用于查询小于或等于某一个值的比例


2. Percent_rank():值占比,值 = ( rank - 1 ) / ( rows - 1 ),此处rank是rank窗口函数对应的值,如果未指定partition_clause, rows为总行数;如果指定了partition_clause,rows则分别各自对应每一个分区的总行数


Select row_number() over w as row_id

, cume_dist() over w as cd_value

, rank() over w as rank_id

, percent_rank() over w as pr_value

, studid

, classid

, score

from t_stud

window w as (partition by classid order by score desc) ;


这里 partition_clause partition by classid


对照上述返回结果分析,对应返回结果的row_id为4,5,6的后三行,因为按照partition_clause与order_clause的定义,>= 56的行数为6;或者也可以参考>=56的row_id的最大值6,该分区总行数是7,所以cume_dist的累计分布值为6/7=0.8571428571428571; percent_rank 的值,依据公式 ( 4 - 1 ) / ( 7 - 1 ) = 0.5

三:前后函数

前后函数,主要包含如下两个窗口函数。可以解决类似需求为求前后值差异,增加或减少。

1. Lag(expr [, N[, default]]) :返回当前行的N行的expr的值

2. LEAD(expr [, N[, default]]) :返回当前行的N行的expr的值


Select studid

, classid

, score

, lag(score) over w as 'lag'

, score - lag(score) over w as 'lag diff'

, lead(score) over w as 'lead'

, score - lead(score) over w as 'lead diff'

from t_stud

window w as (partition by classid order by score desc ) ;


返回结果如下:


对照上述返回结果分析,第一行,对于lag,前一行的score的值并不存在,故为Null;对于lead,后一行的score的值,为99; 第六行,对于lag,前一行的score的值,为89;对于lead,后一行的score的值并不存在,故为Null。上面的SQL也很好的解决了需要计算前后值差多少的需求,当然,也可以采用表自联结的方式实现,但是采用窗口函数,简洁,而且高效。

上面的例子中,我们发现,如果没有找到前一行或者后一行的值,为Null。但是,有时候,我们不希望处理Null值,那么可以用特殊值来替代。那么我们尝试查找前后3行,在未找到的时候,用-1替代。SQL如下:


Select studid

, classid

, score

, lag(score,3,-1) over w as 'lag'

, lead(score,3,-1) over w as 'lead'

from t_stud

window w as (partition by classid order by score desc ) ;


返回结果如下:


+------------+--------------+----------+----------+----------+

| studid | classid | score | lag | lead |

+------------+--------------+----------+----------+----------+

| 1003 | 1 | 100 | -1 | 89 |

| 1002 | 1 | 99 | -1 | 89 |

| 1001 | 1 | 98 | -1 | 89 |

| 1004 | 1 | 89 | 100 | -1 |

| 1005 | 1 | 89 | 99 | -1 |

| 1006 | 1 | 89 | 98 | -1 |

| 1007 | 2 | 99 | -1 | 56 |

| 1010 | 2 | 99 | -1 | 56 |

| 1011 | 2 | 90 | -1 | 56 |

| 1009 | 2 | 56 | 99 | 34 |

| 1012 | 2 | 56 | 99 | -1 |

| 1013 | 2 | 56 | 90 | -1 |

| 1008 | 2 | 34 | 56 | -1 |

+------------+--------------+----------+----------+----------+

分析返回结果,发现在没有找到前/后3行数据的默认值,的确用 -1来替代。


四:首尾函数


首尾函数,主要包含两个窗口函数

1. First_value(expr):返回第一个表达式的值

2. Last_value(expr):返回最后一个表达式的值


Select studid

, classid

, score

, first_value(score) over w as 'first'

, last_value(score) over w as 'last'

from t_stud

window w as (partition by classid order by score desc ) ;


返回结果如下:


+------------+--------------+----------+----------+----------+

| studid | classid | score | first | last |

+------------+--------------+----------+----------+----------+

| 1003 | 1 | 100 | 100 | 100 |

| 1002 | 1 | 99 | 100 | 99 |

| 1001 | 1 | 98 | 100 | 98 |

| 1004 | 1 | 89 | 100 | 89 |

| 1005 | 1 | 89 | 100 | 89 |

| 1006 | 1 | 89 | 100 | 89 |

| 1007 | 2 | 99 | 99 | 99 |

| 1010 | 2 | 99 | 99 | 99 |

| 1011 | 2 | 90 | 99 | 90 |

| 1009 | 2 | 56 | 99 | 56 |

| 1012 | 2 | 56 | 99 | 56 |

| 1013 | 2 | 56 | 99 | 56 |

| 1008 | 2 | 34 | 99 | 34 |

+------------+--------------+----------+----------+----------+


对照上述返回结果分析,First_value的确是返回了最大的一个值,但是,Last_value,这个的返回与预期值很大,而且,再仔细观察一下,发现,last_value与score值一样,并无区别。那么,这是怎么一回事呢?

别急,关于这点,在后面会有具体的分析与讲解。

下面,先让我们继续学习完剩余的窗口函数。


五:其它函数

其它函数,主要包含两个窗口函数。

1. NTH_VALUE(expr, n):返回窗口中第n个expr的值


如下SQL查找第3个score的值


Select studid

, classid

, score

, first_value(score) over w as 'first'

, nth_value(score,3) over w as 'nth'

from t_stud

window w as (partition by classid order by score desc ) ;


返回结果如下:


+------------+--------------+----------+----------+----------+

| studid | classid | score | first | nth |

+------------+--------------+----------+----------+----------+

| 1003 | 1 | 100 | 100 | NULL |

| 1002 | 1 | 99 | 100 | NULL |

| 1001 | 1 | 98 | 100 | 98 |

| 1004 | 1 | 89 | 100 | 98 |

| 1005 | 1 | 89 | 100 | 98 |

| 1006 | 1 | 89 | 100 | 98 |

| 1007 | 2 | 99 | 99 | NULL |

| 1010 | 2 | 99 | 99 | NULL |

| 1011 | 2 | 90 | 99 | 90 |

| 1009 | 2 | 56 | 99 | 90 |

| 1012 | 2 | 56 | 99 | 90 |

| 1013 | 2 | 56 | 99 | 90 |

| 1008 | 2 | 34 | 99 | 90 |

+------------+--------------+----------+----------+----------+



2. NTILE(n):将partition_clause指定的分区划分为N个桶,而分区中的数据平均分到桶中,并返回其分区中当前行的桶编号,但因为可以不被N整除,所以可能数据并不能被完全平均分配。


select studid

, classid

, score

, ntile(4) over w as 'ntile'

from t_stud

window w as (partition by classid order by score desc ) ;


返回结果如下:


+------------+--------------+----------+----------+

| studid | classid | score | ntile |

+------------+--------------+----------+----------+

| 1003 | 1 | 100 | 1 |

| 1002 | 1 | 99 | 1 |

| 1001 | 1 | 98 | 2 |

| 1004 | 1 | 89 | 2 |

| 1005 | 1 | 89 | 3 |

| 1006 | 1 | 89 | 4 |

| 1007 | 2 | 99 | 1 |

| 1010 | 2 | 99 | 1 |

| 1011 | 2 | 90 | 2 |

| 1009 | 2 | 56 | 2 |

| 1012 | 2 | 56 | 3 |

| 1013 | 2 | 56 | 3 |

| 1008 | 2 | 34 | 4 |

+------------+--------------+----------+----------+


以上就是MySQL 8.0版本提供的窗口函数的基本用法,不知道是否解释明白了呢?


小A:十分感谢面试官对于我的不足之处的指点,不过我记得您说后续会解释那个last_value疑惑的,能否请再给我解释一下呢?


面试官:好的,解释这个之前,我不得不提一下,还记得前面提过的窗口函数的完整语法吗?请再观察一下前面所有窗口函数的样例SQL脚本,发现没有?是否到目前为之,都没有包含

frame_clause子句? 下面就仔细地探讨一下frame_clause子句的影响。


六:frame_clause子句的影响


让我们再仔细看看这个子句的相关完整语法:


frame_clause:

frame_units frame_extent

frame_units:

{ROWS | RANGE}


frame_extent:

{frame_start | frame_between}

frame_between:

BETWEEN frame_start AND frame_end

frame_start, frame_end: {

CURRENT ROW

| UNBOUNDED PRECEDING

| UNBOUNDED FOLLOWING

| expr PRECEDING

| expr FOLLOWING

}


首先,我们需要理解frame_units的两个参数的真正含义:


Rows:The frame is defined by beginning and ending row positions. Offsets are differences in row numbers from the current row number.

MySQL官网定义如上。这个指的是物理范围,定义为排序后的指定的开始行到指定的结束行。换句话讲,就是我们最容易理解的要获取从第几行到第几行的意思。


Range:The frame is defined by rows within a value range. Offsets are differences in row values from the current row value.

MySQL官网定义如上。而这个指的是逻辑范围,是依据定义为相同的值被视作是同一行,与排序后的行号无关,而取定的范围与值有关。


另外,再加上后面具体的范围定义子句,则数据的返回会受到相应的影响。


下面,让我们先看一个简单的例子做比较:


Select studid

, classid

, score

, max(score) over(partition by classid order by score desc rows between 1 preceding and 2 following ) as 'max_row'

, max(score) over(partition by classid order by score desc range between 1 preceding and 2 following ) as 'max_range'

from t_stud;


返回结果如下:


+------------+-------------+-----------+---------------+-----------------+

| studid | classid | score | max_row | max_range |

+------------+-------------+-----------+---------------+-----------------+

| 1003 | 1 | 100 | 100 | 100 |

| 1002 | 1 | 99 | 100 | 100 |

| 1001 | 1 | 98 | 99 | 99 |

| 1004 | 1 | 89 | 98 | 89 |

| 1005 | 1 | 89 | 89 | 89 |

| 1006 | 1 | 89 | 89 | 89 |

| 1007 | 2 | 99 | 99 | 99 |

| 1010 | 2 | 99 | 99 | 99 |

| 1011 | 2 | 90 | 99 | 90 |

| 1009 | 2 | 56 | 90 | 56 |

| 1012 | 2 | 56 | 56 | 56 |

| 1013 | 2 | 56 | 56 | 56 |

| 1008 | 2 | 34 | 56 | 34 |

+------------+-------------+-----------+---------------+-----------------+


仔细观察上述的返回结果,来仔细理解子句的影响。


首先,我们的SQL语句分别定义了rows和range两种模式,并且范围均是前一行到后两行。但是因为rows是物理范围,则,针对studid = 1009的这一行,其排序排在此行的前一行值为score = 90,排序排在此行的后面的两行均为score = 56,故此,最大值为90。


而range为逻辑范围,则发现该行对应的值为score = 56,则前一行应该为score = 56+1 = 57,后面两行应该为score = 56 - 2 = 54,则在54到57的范围内,真实值为56,故此,最大值为56。


下面我们再次验证range:


Select studid

, classid

, score

, max(score) over(partition by classid order by score desc range between 34 preceding and 2 following ) as 'max_34'

, max(score) over(partition by classid order by score desc range between 33 preceding and 2 following ) as 'max_33'

from t_stud;


+-----------+---------------+----------+-------------+-----------+

| studid | classid | score | max_34 | max_33 |

+-----------+---------------+----------+-------------+-----------+

| 1003 | 1 | 100 | 100 | 100 |

| 1002 | 1 | 99 | 100 | 100 |

| 1001 | 1 | 98 | 100 | 100 |

| 1004 | 1 | 89 | 100 | 100 |

| 1005 | 1 | 89 | 100 | 100 |

| 1006 | 1 | 89 | 100 | 100 |

| 1007 | 2 | 99 | 99 | 99 |

| 1010 | 2 | 99 | 99 | 99 |

| 1011 | 2 | 90 | 99 | 99 |

| 1009 | 2 | 56 | 90 | 56 |

| 1012 | 2 | 56 | 90 | 56 |

| 1013 | 2 | 56 | 90 | 56 |

| 1008 | 2 | 34 | 56 | 56 |

+-----------+---------------+----------+-------------+-----------+


仔细观察上述的返回结果

此次我们均使用的是range模式,仅仅不同之处在于开始位置,一个前导34,一个前导33。


首先,针对前导34,发现该行对应的值为score = 56,则前导34行应该为score = 56+34 = 90,后面两行应该为score = 56 - 2 = 54,则在54到90的范围内,因为存在真实值为90,故此,最大值为90。


其次,针对前导33,发现该行对应的值为score = 56,则前导33行应该为score = 56+33 = 89,后面两行应该为score = 56 - 2 = 54,则在54到89的范围内,因为不存在值为89,故此,最大值为56。


如上是针对明确给出参数的情况,但是为什么前面的last_value并未明确给出参数,也会导致与预期不同的情况呢?这,就不得不提一下有无order_clause的情况下frame_clause的默认值情况了。



MySQL官网是这么解释的:


In the absence of a frame clause, the default frame depends on whether an ORDER BY clause is present:


With ORDER BY: The default frame includes rows from the partition start through the current row, including all peers of the current row (rows equal to the current row according to the ORDER BY clause). The default is equivalent to this frame specification:


RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW


Without ORDER BY: The default frame includes all partition rows (because, without ORDER BY, all partition rows are peers). The default is equivalent to this frame specification:


RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING


讲解到这里,我们回过头来看看先前的last_value(),其中含有order_clause子句,但是没有指定frame_clause子句,那么实际上,就是逻辑范围查询,从前导无边界到当前行,则也就不难理解为什么其返回值都和score值相同了。


最后,需要重点提及的是,frame_clause子句目前仅仅影响到如下窗口函数和聚合函数的使用:first_value(),last_value(),nth_value(), max(), min(), avg(), sum(), count()。


以上,就是窗口函数的基本使用和注意事项。


小A:再次感谢面试官的解惑,让我了解了强大的窗口函数功能,回去之后,我一定会仔细学习,加深了解和使用窗口函数,写出更加简洁,高效的SQL脚本。

展开阅读全文

页面更新:2024-04-15

标签:函数   窗口   前导   子句   首尾   名次   仔细   定义   版本   发现

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top