-
前言 -
窗口函數的格式 -
函數(Function)的類型 -
開窗函數over() -
窗口函數使用 -
ROW_NUMBER() -
RANK()與DENSE_RANK() -
LEAD()與LAG() -
FIRST_VALUE()與LAST_VALUE() -
NTILE() -
MAX()、MIN()、AVG()、SUM()與COUNT() -
窗口從句的使用 -
窗口從句進階
前言
窗口函數經常會在leetCode的題目中使用到
窗口函數的格式
Function() over(partition by query_patition_clause
order by order_by_clause Window_clause )
-
排名函數 ROW_NUMBER(); -
排名函數 RANK() 和 DENSE_RANK(); -
錯行函數 lead()、lag(); -
取值函數 First_value()和last_value(); -
分箱函數 NTILE(); -
統計函數,也就是我們常用的聚合函數 MAX()、MIN()、AVG()、SUM()、COUNT()
-
partition by query_patition_clause:即分組,通過query_patition_clause進行分組,一般是表中的某一個字段,所以可以把partition by 看作與GROUP BY 具有相同功能的語法。 -
order by order_by_clause:即排序,通過order_by_clause 進行排序,一般是在分組(partition by)之后再進行排序,如此一來,就是在組內進行排序。如果沒有前面的分組子句(partition by),那么就是全部數據進行排序。和普通MySQL中的查詢語句一樣,排序從句也支持ASC和DESC的用法。 -
Window_clause:窗口從句,它是排序之后的功能擴展,它標識了在排序之后的一個范圍,它的格式是: rows | range between start_expr and end_expr
-
rows是物理范圍,即根據order by子句排序后,取的前N行及后N行的數據計算(與當前行的值無關,只與排序后的行號相關); -
range是邏輯范圍,根據order by子句排序后,指定當前行對應值的范圍取值,行數不固定,只要行值在范圍內,對應行都包含在內
-
unbounded preceding:指明窗口開始于分組的第一行,以排序之后的第一行為起點; -
current row:以當前行為起點; -
n preceding:以當前行的前面第n行為起點; -
n following:以當前行的后面第n行為起點;
-
unbounded following:以排序之后的最后一行為終點; -
current row:以當前行為終點; -
n preceding:以當前行的前面第n行為終點; -
n following:以當前行的后面第n行為終點;
create table test(id int,name varchar(10),sale int);
insert into test values(1,'aaa',100);
insert into test values(1,'bbb',200);
insert into test values(1,'ccc',200);
insert into test values(1,'ddd',300);
insert into test values(2,'eee',400);
insert into test values(2,'fff',200);
表中的數據為:
mysql> select * from test;
+------+------+------+
| id | name | sale |
+------+------+------+
| 1 | aaa | 100 |
| 1 | bbb | 200 |
| 1 | ccc | 200 |
| 1 | ddd | 300 |
| 2 | eee | 400 |
| 2 | fff | 200 |
+------+------+------+
ROW_NUMBER()
row_number() over(partition by col1 order by col2)
mysql> #對id進行分組,同一個組內的數據再根據sale進行排序,這個排序序號是唯一并且連續的
mysql> select t.*,row_number() over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 3 |
| 1 | ddd | 300 | 4 |
| 2 | fff | 200 | 1 |
| 2 | eee | 400 | 2 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #當沒有partition by分組從句時,將視全部記錄為一個分組
mysql> select t.*,row_number() over(order by sale) as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 3 |
| 2 | fff | 200 | 4 |
| 1 | ddd | 300 | 5 |
| 2 | eee | 400 | 6 |
+------+------+------+-------+
6 rows in set (0.00 sec)
RANK()與DENSE_RANK()
rank() over(partition by col1 order by col2)
dense_rank() over(partition by col1 order by col2)
mysql> #對id進行分組,分組后根據sale排序
mysql> #可以發現sale相同時有相同的序號,并且由于id=1的分組中沒有排名第3的序號造成排序不連續
mysql> select t.*,rank() over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 2 |
| 1 | ddd | 300 | 4 |
| 2 | fff | 200 | 1 |
| 2 | eee | 400 | 2 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #沒有分組,只根據sale排序,sale相同時有相同的序號,沒有排名3和4造成排序不連續
mysql> select t.*,rank() over(order by sale) as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 2 |
| 2 | fff | 200 | 2 |
| 1 | ddd | 300 | 5 |
| 2 | eee | 400 | 6 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #對id進行分組,分組后根據sale排序
mysql> #可以發現sale相同時有相同的序號,但是整個排序序號是連續的
mysql> select t.*,dense_rank() over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 2 |
| 1 | ddd | 300 | 3 |
| 2 | fff | 200 | 1 |
| 2 | eee | 400 | 2 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #沒有分組,只根據sale排序,sale相同時有相同的序號,整個排序序號是連續的
mysql> select t.*,dense_rank() over(order by sale) as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 2 |
| 2 | fff | 200 | 2 |
| 1 | ddd | 300 | 3 |
| 2 | eee | 400 | 4 |
+------+------+------+-------+
6 rows in set (0.00 sec)
-
row_number是沒有重復的一種排序,即使對于兩行相同的數據,也會根據查詢到的順序進行排名;而rank函數和dense_rank函數對相同的數據會有一個相同的次序; -
rank函數的排序是可能不連續的,dense_rank函數的排序是連續的
lead(EXPR,<OFFSET>,<DEFAULT>) over(partition by col1 order by col2)
lag(EXPR,<OFFSET>,<DEFAULT>) over(partition by col1 order by col2)
其中:
-
EXPR通常是直接是列名,也可以是從其他行返回的表達式; -
OFFSET是默認為1,表示在當前分區內基于當前行的偏移行數; -
DEFAULT是在OFFSET指定的偏移行數超出了分組的范圍時(因為默認會返回null),可以通過設置這個字段來返回一個默認值來替代null。
mysql> #為每一行數據的下一行數據進行開窗,如果該行沒有下一行數據,則顯示為NULL
mysql> select t.*,lead(sale) over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 200 | <--下一行的sale值為200,開窗結果為200
| 1 | bbb | 200 | 200 | <--下一行的sale值為200,開窗結果為200
| 1 | ccc | 200 | 300 | <--下一行的sale值為300,開窗結果為300
| 1 | ddd | 300 | NULL | <--已經是最后一行,沒有下一行數據,開窗結果為NULL
| 2 | fff | 200 | 400 |
| 2 | eee | 400 | NULL |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #為每一行數據的上一行數據進行開窗,如果該行沒有上一行數據,則顯示為NULL
mysql> select t.*,lag(sale) over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | NULL | <--當前行為第一行,沒有上一行數據,開窗結果為NULL
| 1 | bbb | 200 | 100 | <--上一行的sale值為100,開窗結果為100
| 1 | ccc | 200 | 200 | <--上一行的sale值為200,開窗結果為200
| 1 | ddd | 300 | 200 | <--上一行的sale值為200,開窗結果為200
| 2 | fff | 200 | NULL |
| 2 | eee | 400 | 200 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> select t.*,lead(sale,2) over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 200 | <--下2行的sale值為200,開窗結果為200
| 1 | bbb | 200 | 300 | <--下2行的sale值為300,開窗結果為300
| 1 | ccc | 200 | NULL | <--已經是倒數第2行,沒有下2行的數據,開窗結果為NULL
| 1 | ddd | 300 | NULL | <--已經是最后一行,沒有下2行的數據,開窗結果為NULL
| 2 | fff | 200 | NULL |
| 2 | eee | 400 | NULL |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> select t.*,lead(sale,2,"Empty") over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 200 |
| 1 | bbb | 200 | 300 |
| 1 | ccc | 200 | Empty | <--已經是倒數第2行,沒有下2行的數據,開窗結果為"Empty"
| 1 | ddd | 300 | Empty | <--已經是最后一行,沒有下2行的數據,開窗結果為"Empty"
| 2 | fff | 200 | Empty |
| 2 | eee | 400 | Empty |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #存在下一行數據顯示為Exist,不存在下一行數據則顯示NULL,這個NULL是默認的
mysql> select t.*,lead("Exist") over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | Exist | <--下一行的數據存在,開窗結果為"Exist"
| 1 | bbb | 200 | Exist | <--下一行的數據存在,開窗結果為"Exist"
| 1 | ccc | 200 | Exist | <--下一行的數據存在,開窗結果為"Exist"
| 1 | ddd | 300 | NULL | <--已經是最后一行,沒有下一行數據,開窗結果為NULL
| 2 | fff | 200 | Exist |
| 2 | eee | 400 | NULL |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #存在下一行數據顯示為Exist,不存在下一行數據則顯示Empty
mysql> select t.*,lead("Exist",1,"Empty") over(partition by id order by sale)
-> as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | Exist | <--下一行的數據存在,開窗結果為"Exist"
| 1 | bbb | 200 | Exist | <--下一行的數據存在,開窗結果為"Exist"
| 1 | ccc | 200 | Exist | <--下一行的數據存在,開窗結果為"Exist"
| 1 | ddd | 300 | Empty | <--已經是最后一行,沒有下一行數據,開窗結果為"Empty"
| 2 | fff | 200 | Exist |
| 2 | eee | 400 | Empty |
+------+------+------+-------+
6 rows in set (0.00 sec)
first_value( EXPR ) over( partition by col1 order by col2 )
last_value( EXPR ) over( partition by col1 order by col2 )
mysql> #first_value函數查看每一個分組的第一個值
mysql> select t.*,first_value(sale) over(partition by id) as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 100 | <--分組的第一個值為100,開窗結果100
| 1 | bbb | 200 | 100 | <--分組的第一個值為100,開窗結果100
| 1 | ccc | 200 | 100 | <--分組的第一個值為100,開窗結果100
| 1 | ddd | 300 | 100 | <--分組的第一個值為100,開窗結果100
| 2 | eee | 400 | 400 |
| 2 | fff | 200 | 400 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #對id進行分組,同一個組內的數據再根據sale進行排序,查看每一個分組的第一個值
mysql> select t.*,first_value(sale) over(partition by id order by sale)
-> as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 100 | <--分組排序之后的第一個值為100,開窗結果100
| 1 | bbb | 200 | 100 | <--分組排序之后的第一個值為100,開窗結果100
| 1 | ccc | 200 | 100 | <--分組排序之后的第一個值為100,開窗結果100
| 1 | ddd | 300 | 100 | <--分組排序之后的第一個值為100,開窗結果100
| 2 | fff | 200 | 200 |
| 2 | eee | 400 | 200 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #last_value函數查看每一個分組的最后一個值
mysql> select t.*,last_value(sale) over(partition by id) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 300 | <--分組排序之后的最后一個值為300,開窗結果300
| 1 | bbb | 200 | 300 | <--分組排序之后的最后一個值為300,開窗結果300
| 1 | ccc | 200 | 300 | <--分組排序之后的最后一個值為300,開窗結果300
| 1 | ddd | 300 | 300 | <--分組排序之后的最后一個值為300,開窗結果300
| 2 | eee | 400 | 200 |
| 2 | fff | 200 | 200 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #對id進行分組,同一個組內的數據再根據sale進行排序,查看每一個分組的最后一個值
mysql> #但是你發現id=1的組每一行顯示的不是300,id=2的分組每一行顯示的不是400
mysql> select t.*,last_value(sale) over(partition by id order by sale) as rank1
-> from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 100 |
| 1 | bbb | 200 | 200 |
| 1 | ccc | 200 | 200 |
| 1 | ddd | 300 | 300 |
| 2 | fff | 200 | 200 |
| 2 | eee | 400 | 400 |
+------+------+------+-------+
6 rows in set (0.00 sec)
-
查詢到第1行sale=100,只有當前一行,最后一個值只有100,開窗結果為100; -
查詢到第2行sale=100,200兩個數據,最后一個值是200,開窗結果為200; -
查詢到第3行sale=100,200,200三個數據,最后一個值是200,開窗結果為200; -
查詢到四行sale=100,200,200,300四個數據,最后一個值是300,開窗結果為300,至此id=1的分組查詢完畢
ntile(ntile_num) OVER ( partition by col1 order by col2 )
mysql> 給所有數據分配四個桶
mysql> select t.*,ntile(4) over(partition by id order by sale) as rank1 from test as t;
+------+------+------+-------+
| id | name | sale | rank1 |
+------+------+------+-------+
| 1 | aaa | 100 | 1 |
| 1 | bbb | 200 | 2 |
| 1 | ccc | 200 | 3 |
| 1 | ddd | 300 | 4 |
| 2 | fff | 200 | 1 |
| 2 | eee | 400 | 2 |
+------+------+------+-------+
6 rows in set (0.00 sec)
max(EXPR) over(partition by col1 order by col2)
min(EXPR) over(partition by col1 order by col2)
avg(EXPR) over(partition by col1 order by col2)
sum(EXPR) over(partition by col1 order by col2)
count(EXPR) over(partition by col1 order by col2)
mysql> create table test( id int, val int );
mysql> insert into test values(1,1),(1,2),(1,3),(1,4),(1,5),(2,6),
-> (2,7),(2,8),(2,9),(1,3),(1,5);
mysql> select * from test;
+------+------+
| id | val |
+------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 2 | 6 |
| 2 | 7 |
| 2 | 8 |
| 2 | 9 |
| 1 | 3 |
| 1 | 5 |
+------+------+
11 rows in set (0.00 sec)
只有分組,沒有排序,顯示分組的最大值
mysql> select t.*,max(val) over(partition by id) as MAX from test as t;
+------+------+------+
| id | val | MAX |
+------+------+------+
| 1 | 1 | 5 |
| 1 | 2 | 5 |
| 1 | 3 | 5 |
| 1 | 4 | 5 |
| 1 | 5 | 5 |
| 1 | 3 | 5 |
| 1 | 5 | 5 |
| 2 | 6 | 9 |
| 2 | 7 | 9 |
| 2 | 8 | 9 |
| 2 | 9 | 9 |
+------+------+------+
11 rows in set (0.00 sec)
mysql> select t.*,max(val) over(partition by id order by val) as MAX
-> from test as t;
+------+------+------+
| id | val | MAX |
+------+------+------+
| 1 | 1 | 1 | <--第1行的最大值是1,所以顯示1
| 1 | 2 | 2 | <--前面2行的最大值是2,所以顯示2
| 1 | 3 | 3 | <--前面3行的最大值是3,所以顯示3
| 1 | 3 | 3 | <--前面4行的最大值是3,所以顯示3
| 1 | 4 | 4 | <--前面5行的最大值是4,所以顯示4
| 1 | 5 | 5 | <--前面6行的最大值是5,所以顯示5
| 1 | 5 | 5 | <--前面7行的最大值是5,所以顯示5
| 2 | 6 | 6 |
| 2 | 7 | 7 |
| 2 | 8 | 8 |
| 2 | 9 | 9 |
+------+------+------+
11 rows in set (0.00 sec)
其實,在上面這個代碼中,完整的顯示是這樣的:
mysql> select t.*,max(val) over(partition by id order by val range between unbounded preceding and current row)
-> as MAX from test as t;
+------+------+------+
| id | val | MAX |
+------+------+------+
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | 3 | 3 |
| 1 | 3 | 3 |
| 1 | 4 | 4 |
| 1 | 5 | 5 |
| 1 | 5 | 5 |
| 2 | 6 | 6 |
| 2 | 7 | 7 |
| 2 | 8 | 8 |
| 2 | 9 | 9 |
+------+------+------+
11 rows in set (0.00 sec)
其中代碼
range between unbounded preceding and current row
rows between unbounded preceding and current row
mysql> select t.*,max(val) over(partition by id order by val rows between unbounded preceding and unbounded following) as MAX
-> from test as t;
+------+------+------+
| id | val | MAX |
+------+------+------+
| 1 | 1 | 5 |
| 1 | 2 | 5 |
| 1 | 3 | 5 |
| 1 | 3 | 5 |
| 1 | 4 | 5 |
| 1 | 5 | 5 |
| 1 | 5 | 5 |
| 2 | 6 | 9 |
| 2 | 7 | 9 |
| 2 | 8 | 9 |
| 2 | 9 | 9 |
+------+------+------+
11 rows in set (0.00 sec)
rows between unbounded preceding and unbounded following
mysql> #分組之后沒有排序,就沒有默認的窗口子句,得到的結果是每一組的最大值
mysql> select t.*,sum(val) over(partition by id) as SUM from test as t;
+------+------+------+
| id | val | SUM |
+------+------+------+
| 1 | 1 | 23 |
| 1 | 2 | 23 |
| 1 | 3 | 23 |
| 1 | 4 | 23 |
| 1 | 5 | 23 |
| 1 | 3 | 23 |
| 1 | 5 | 23 |
| 2 | 6 | 30 |
| 2 | 7 | 30 |
| 2 | 8 | 30 |
| 2 | 9 | 30 |
+------+------+------+
11 rows in set (0.00 sec)
示例二,同時使用分組和排序:
mysql> #分組并且排序
mysql> #排序如果沒有窗口子句會有一個默認的規則,即range between unbounded preceding and current row
mysql> select t.*,sum(val) over(partition by id order by val)
-> as SUM from test as t;
+------+------+------+
| id | val | SUM |
+------+------+------+
| 1 | 1 | 1 | <--計算前1行的和,開窗結果為1
| 1 | 2 | 3 | <--計算前2行的和,開窗結果為3
| 1 | 3 | 9 | <--計算前3行的和,由于是range邏輯范圍,相同的val看作同一行,所以和為1+2+3+3=9
| 1 | 3 | 9 | <--計算前4行的和,該行和第三行同屬于一行,所以和為9,開窗結果為9
| 1 | 4 | 13 | <--計算前5行的和,開窗結果為13
| 1 | 5 | 23 | <--計算前6行的和,由于是range邏輯范圍,相同的val看作同一行,所以和為23
| 1 | 5 | 23 | <--計算前7行的和,該行和第6行同屬于一行,所以和為23,開窗結果為23
| 2 | 6 | 6 |
| 2 | 7 | 13 |
| 2 | 8 | 21 |
| 2 | 9 | 30 |
+------+------+------+
11 rows in set (0.00 sec)
有興趣的同學可以證明示例二的正確性,在排序之后手動添加窗口子句,一定會得到相同的結果:
mysql> #得到和上面一樣的結果Orz
mysql> select t.*,sum(val) over(partition by id order by val range between unbounded preceding and current row)
-> as SUM from test as t;
+------+------+------+
| id | val | SUM |
+------+------+------+
| 1 | 1 | 1 |
| 1 | 2 | 3 |
| 1 | 3 | 9 |
| 1 | 3 | 9 |
| 1 | 4 | 13 |
| 1 | 5 | 23 |
| 1 | 5 | 23 |
| 2 | 6 | 6 |
| 2 | 7 | 13 |
| 2 | 8 | 21 |
| 2 | 9 | 30 |
+------+------+------+
11 rows in set (0.00 sec)
示例三,同時使用了分組和排序,但是窗口從句使用物理范圍rows:
mysql> select t.*,sum(val) over(partition by id order by val rows between unbounded preceding and current row)
-> as SUM from test as t;
+------+------+------+
| id | val | SUM |
+------+------+------+
| 1 | 1 | 1 | <--計算前1行的和,開窗結果為1
| 1 | 2 | 3 | <--計算前2行的和,開窗結果為3
| 1 | 3 | 6 | <--計算前3行的和,開窗結果為1+2+3=6
| 1 | 3 | 9 | <--計算前4行的和,開窗結果為1+2+3+3=9
| 1 | 4 | 13 | <--計算前5行的和,開窗結果為1+2+3+3+4=13
| 1 | 5 | 18 | <--計算前6行的和,開窗結果為1+2+3+3+4+5=18
| 1 | 5 | 23 | <--計算前7行的和,開窗結果為1+2+3+3+4+5+5=23
| 2 | 6 | 6 |
| 2 | 7 | 13 |
| 2 | 8 | 21 |
| 2 | 9 | 30 |
+------+------+------+
11 rows in set (0.00 sec)
mysql> #使用rows物理范圍
mysql> #使用1 preceding表示當前行的前一行作為起點
mysql> #使用1 following表示當前行的后一行作為終點
mysql> select t.*,max(val) over(partition by id order by val rows between 1 preceding and 1 following)
-> as MAX from test as t;
+------+------+------+
| id | val | MAX |
+------+------+------+
| 1 | 1 | 2 | <--前一行NULL、當前行1、后一行2,比較而得的最大值,開窗結果為2
| 1 | 2 | 3 | <--前一行1、當前行2、后一行3,比較而得的最大值,開窗結果為3
| 1 | 3 | 3 | <--前一行2、當前行3、后一行3,比較而得的最大值,開窗結果為3
| 1 | 3 | 4 | <--前一行3、當前行3、后一行4,比較而得的最大值,開窗結果為4
| 1 | 4 | 5 | <--前一行3、當前行4、后一行5,比較而得的最大值,開窗結果為5
| 1 | 5 | 5 | <--前一行4、當前行5、后一行5,比較而得的最大值,開窗結果為5
| 1 | 5 | 5 | <--前一行5、當前行5、后一行NULL,比較而得的最大值,開窗結果為5
| 2 | 6 | 7 |
| 2 | 7 | 8 |
| 2 | 8 | 9 |
| 2 | 9 | 9 |
+------+------+------+
11 rows in set (0.00 sec)
再來試試使用range邏輯范圍,會產生什么奇妙的結果,這次我們使用sum函數
mysql> #使用range邏輯范圍
mysql> #使用1 preceding表示當前行的前一行作為起點
mysql> #使用1 following表示當前行的后一行作為終點
mysql> select t.*,sum(val) over(partition by id order by val range between 1 preceding and 1 following)
-> as SUM from test as t;
+------+------+------+
| id | val | SUM |
+------+------+------+
| 1 | 1 | 3 | <--前一行NULL、當前行1、后一行2,1+2=3
| 1 | 2 | 9 | <--前一行1、當前行2、后一行有2個相同的值,邏輯上規定為同一行的3,1+2+3+3=9
| 1 | 3 | 12 | <--前一行2、當前行有2個相同的值,邏輯上規定為同一行的3、后一行4,2+3+3+4=12
| 1 | 3 | 12 | <--前一行2、當前行有2個相同的值,邏輯上規定為同一行的3、后一行4,2+3+3+4=12
| 1 | 4 | 20 | <--前一行有2個相同的值,邏輯上規定為同一行的3、當前行4、后一行有2個相同的值,邏輯上規定為同一行的5,3+3+4+5+5=20
| 1 | 5 | 14 | <--前一行4、當前行有2個相同的值,邏輯上規定為同一行的5、后一行NULL,4+5+5=14
| 1 | 5 | 14 | <--前一行4、當前行有2個相同的值,邏輯上規定為同一行的5、后一行NULL,4+5+5=14
| 2 | 6 | 13 |
| 2 | 7 | 21 |
| 2 | 8 | 24 |
| 2 | 9 | 17 |
+------+------+------+
11 rows in set (0.00 sec)
作者:五四青年
來源:https://zhuanlan.zhihu.com/p/514345120






