百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

MySQL之窗口函数

toyiye 2024-06-21 12:14 10 浏览 0 评论

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脚本。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码