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_name

w

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

MySQL之視窗函式

對照上述返回結果分析,對應返回結果的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 ) ;

返回結果如下:

MySQL之視窗函式

對照上述返回結果分析,第一行,對於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指令碼。