-
前言 -
窗口函數(shù)的格式 -
函數(shù)(Function)的類型 -
開窗函數(shù)over() -
窗口函數(shù)使用 -
ROW_NUMBER() -
RANK()與DENSE_RANK() -
LEAD()與LAG() -
FIRST_VALUE()與LAST_VALUE() -
NTILE() -
MAX()、MIN()、AVG()、SUM()與COUNT() -
窗口從句的使用 -
窗口從句進階
前言
窗口函數(shù)經(jīng)常會在leetCode的題目中使用到
窗口函數(shù)的格式
Function() over(partition by query_patition_clause
order by order_by_clause Window_clause )
-
排名函數(shù) ROW_NUMBER(); -
排名函數(shù) RANK() 和 DENSE_RANK(); -
錯行函數(shù) lead()、lag(); -
取值函數(shù) First_value()和last_value(); -
分箱函數(shù) NTILE(); -
統(tǒng)計函數(shù),也就是我們常用的聚合函數(shù) 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)之后再進行排序,如此一來,就是在組內(nèi)進行排序。如果沒有前面的分組子句(partition by),那么就是全部數(shù)據(jù)進行排序。和普通MySQL中的查詢語句一樣,排序從句也支持ASC和DESC的用法。 -
Window_clause:窗口從句,它是排序之后的功能擴展,它標識了在排序之后的一個范圍,它的格式是: rows | range between start_expr and end_expr
-
rows是物理范圍,即根據(jù)order by子句排序后,取的前N行及后N行的數(shù)據(jù)計算(與當(dāng)前行的值無關(guān),只與排序后的行號相關(guān)); -
range是邏輯范圍,根據(jù)order by子句排序后,指定當(dāng)前行對應(yīng)值的范圍取值,行數(shù)不固定,只要行值在范圍內(nèi),對應(yīng)行都包含在內(nèi)
-
unbounded preceding:指明窗口開始于分組的第一行,以排序之后的第一行為起點; -
current row:以當(dāng)前行為起點; -
n preceding:以當(dāng)前行的前面第n行為起點; -
n following:以當(dāng)前行的后面第n行為起點;
-
unbounded following:以排序之后的最后一行為終點; -
current row:以當(dāng)前行為終點; -
n preceding:以當(dāng)前行的前面第n行為終點; -
n following:以當(dāng)前行的后面第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);
表中的數(shù)據(jù)為:
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進行分組,同一個組內(nèi)的數(shù)據(jù)再根據(jù)sale進行排序,這個排序序號是唯一并且連續(xù)的
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> #當(dāng)沒有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進行分組,分組后根據(jù)sale排序
mysql> #可以發(fā)現(xiàn)sale相同時有相同的序號,并且由于id=1的分組中沒有排名第3的序號造成排序不連續(xù)
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> #沒有分組,只根據(jù)sale排序,sale相同時有相同的序號,沒有排名3和4造成排序不連續(xù)
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進行分組,分組后根據(jù)sale排序
mysql> #可以發(fā)現(xiàn)sale相同時有相同的序號,但是整個排序序號是連續(xù)的
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> #沒有分組,只根據(jù)sale排序,sale相同時有相同的序號,整個排序序號是連續(xù)的
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是沒有重復(fù)的一種排序,即使對于兩行相同的數(shù)據(jù),也會根據(jù)查詢到的順序進行排名;而rank函數(shù)和dense_rank函數(shù)對相同的數(shù)據(jù)會有一個相同的次序; -
rank函數(shù)的排序是可能不連續(xù)的,dense_rank函數(shù)的排序是連續(xù)的
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,表示在當(dāng)前分區(qū)內(nèi)基于當(dāng)前行的偏移行數(shù); -
DEFAULT是在OFFSET指定的偏移行數(shù)超出了分組的范圍時(因為默認會返回null),可以通過設(shè)置這個字段來返回一個默認值來替代null。
mysql> #為每一行數(shù)據(jù)的下一行數(shù)據(jù)進行開窗,如果該行沒有下一行數(shù)據(jù),則顯示為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,開窗結(jié)果為200
| 1 | bbb | 200 | 200 | <--下一行的sale值為200,開窗結(jié)果為200
| 1 | ccc | 200 | 300 | <--下一行的sale值為300,開窗結(jié)果為300
| 1 | ddd | 300 | NULL | <--已經(jīng)是最后一行,沒有下一行數(shù)據(jù),開窗結(jié)果為NULL
| 2 | fff | 200 | 400 |
| 2 | eee | 400 | NULL |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #為每一行數(shù)據(jù)的上一行數(shù)據(jù)進行開窗,如果該行沒有上一行數(shù)據(jù),則顯示為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 | <--當(dāng)前行為第一行,沒有上一行數(shù)據(jù),開窗結(jié)果為NULL
| 1 | bbb | 200 | 100 | <--上一行的sale值為100,開窗結(jié)果為100
| 1 | ccc | 200 | 200 | <--上一行的sale值為200,開窗結(jié)果為200
| 1 | ddd | 300 | 200 | <--上一行的sale值為200,開窗結(jié)果為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,開窗結(jié)果為200
| 1 | bbb | 200 | 300 | <--下2行的sale值為300,開窗結(jié)果為300
| 1 | ccc | 200 | NULL | <--已經(jīng)是倒數(shù)第2行,沒有下2行的數(shù)據(jù),開窗結(jié)果為NULL
| 1 | ddd | 300 | NULL | <--已經(jīng)是最后一行,沒有下2行的數(shù)據(jù),開窗結(jié)果為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 | <--已經(jīng)是倒數(shù)第2行,沒有下2行的數(shù)據(jù),開窗結(jié)果為"Empty"
| 1 | ddd | 300 | Empty | <--已經(jīng)是最后一行,沒有下2行的數(shù)據(jù),開窗結(jié)果為"Empty"
| 2 | fff | 200 | Empty |
| 2 | eee | 400 | Empty |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #存在下一行數(shù)據(jù)顯示為Exist,不存在下一行數(shù)據(jù)則顯示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 | <--下一行的數(shù)據(jù)存在,開窗結(jié)果為"Exist"
| 1 | bbb | 200 | Exist | <--下一行的數(shù)據(jù)存在,開窗結(jié)果為"Exist"
| 1 | ccc | 200 | Exist | <--下一行的數(shù)據(jù)存在,開窗結(jié)果為"Exist"
| 1 | ddd | 300 | NULL | <--已經(jīng)是最后一行,沒有下一行數(shù)據(jù),開窗結(jié)果為NULL
| 2 | fff | 200 | Exist |
| 2 | eee | 400 | NULL |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #存在下一行數(shù)據(jù)顯示為Exist,不存在下一行數(shù)據(jù)則顯示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 | <--下一行的數(shù)據(jù)存在,開窗結(jié)果為"Exist"
| 1 | bbb | 200 | Exist | <--下一行的數(shù)據(jù)存在,開窗結(jié)果為"Exist"
| 1 | ccc | 200 | Exist | <--下一行的數(shù)據(jù)存在,開窗結(jié)果為"Exist"
| 1 | ddd | 300 | Empty | <--已經(jīng)是最后一行,沒有下一行數(shù)據(jù),開窗結(jié)果為"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函數(shù)查看每一個分組的第一個值
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,開窗結(jié)果100
| 1 | bbb | 200 | 100 | <--分組的第一個值為100,開窗結(jié)果100
| 1 | ccc | 200 | 100 | <--分組的第一個值為100,開窗結(jié)果100
| 1 | ddd | 300 | 100 | <--分組的第一個值為100,開窗結(jié)果100
| 2 | eee | 400 | 400 |
| 2 | fff | 200 | 400 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #對id進行分組,同一個組內(nèi)的數(shù)據(jù)再根據(jù)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,開窗結(jié)果100
| 1 | bbb | 200 | 100 | <--分組排序之后的第一個值為100,開窗結(jié)果100
| 1 | ccc | 200 | 100 | <--分組排序之后的第一個值為100,開窗結(jié)果100
| 1 | ddd | 300 | 100 | <--分組排序之后的第一個值為100,開窗結(jié)果100
| 2 | fff | 200 | 200 |
| 2 | eee | 400 | 200 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #last_value函數(shù)查看每一個分組的最后一個值
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,開窗結(jié)果300
| 1 | bbb | 200 | 300 | <--分組排序之后的最后一個值為300,開窗結(jié)果300
| 1 | ccc | 200 | 300 | <--分組排序之后的最后一個值為300,開窗結(jié)果300
| 1 | ddd | 300 | 300 | <--分組排序之后的最后一個值為300,開窗結(jié)果300
| 2 | eee | 400 | 200 |
| 2 | fff | 200 | 200 |
+------+------+------+-------+
6 rows in set (0.00 sec)
mysql> #對id進行分組,同一個組內(nèi)的數(shù)據(jù)再根據(jù)sale進行排序,查看每一個分組的最后一個值
mysql> #但是你發(fā)現(xiàn)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,只有當(dāng)前一行,最后一個值只有100,開窗結(jié)果為100; -
查詢到第2行sale=100,200兩個數(shù)據(jù),最后一個值是200,開窗結(jié)果為200; -
查詢到第3行sale=100,200,200三個數(shù)據(jù),最后一個值是200,開窗結(jié)果為200; -
查詢到四行sale=100,200,200,300四個數(shù)據(jù),最后一個值是300,開窗結(jié)果為300,至此id=1的分組查詢完畢
ntile(ntile_num) OVER ( partition by col1 order by col2 )
mysql> 給所有數(shù)據(jù)分配四個桶
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> #分組之后沒有排序,就沒有默認的窗口子句,得到的結(jié)果是每一組的最大值
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> #排序如果沒有窗口子句會有一個默認的規(guī)則,即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行的和,開窗結(jié)果為1
| 1 | 2 | 3 | <--計算前2行的和,開窗結(jié)果為3
| 1 | 3 | 9 | <--計算前3行的和,由于是range邏輯范圍,相同的val看作同一行,所以和為1+2+3+3=9
| 1 | 3 | 9 | <--計算前4行的和,該行和第三行同屬于一行,所以和為9,開窗結(jié)果為9
| 1 | 4 | 13 | <--計算前5行的和,開窗結(jié)果為13
| 1 | 5 | 23 | <--計算前6行的和,由于是range邏輯范圍,相同的val看作同一行,所以和為23
| 1 | 5 | 23 | <--計算前7行的和,該行和第6行同屬于一行,所以和為23,開窗結(jié)果為23
| 2 | 6 | 6 |
| 2 | 7 | 13 |
| 2 | 8 | 21 |
| 2 | 9 | 30 |
+------+------+------+
11 rows in set (0.00 sec)
有興趣的同學(xué)可以證明示例二的正確性,在排序之后手動添加窗口子句,一定會得到相同的結(jié)果:
mysql> #得到和上面一樣的結(jié)果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行的和,開窗結(jié)果為1
| 1 | 2 | 3 | <--計算前2行的和,開窗結(jié)果為3
| 1 | 3 | 6 | <--計算前3行的和,開窗結(jié)果為1+2+3=6
| 1 | 3 | 9 | <--計算前4行的和,開窗結(jié)果為1+2+3+3=9
| 1 | 4 | 13 | <--計算前5行的和,開窗結(jié)果為1+2+3+3+4=13
| 1 | 5 | 18 | <--計算前6行的和,開窗結(jié)果為1+2+3+3+4+5=18
| 1 | 5 | 23 | <--計算前7行的和,開窗結(jié)果為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表示當(dāng)前行的前一行作為起點
mysql> #使用1 following表示當(dāng)前行的后一行作為終點
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、當(dāng)前行1、后一行2,比較而得的最大值,開窗結(jié)果為2
| 1 | 2 | 3 | <--前一行1、當(dāng)前行2、后一行3,比較而得的最大值,開窗結(jié)果為3
| 1 | 3 | 3 | <--前一行2、當(dāng)前行3、后一行3,比較而得的最大值,開窗結(jié)果為3
| 1 | 3 | 4 | <--前一行3、當(dāng)前行3、后一行4,比較而得的最大值,開窗結(jié)果為4
| 1 | 4 | 5 | <--前一行3、當(dāng)前行4、后一行5,比較而得的最大值,開窗結(jié)果為5
| 1 | 5 | 5 | <--前一行4、當(dāng)前行5、后一行5,比較而得的最大值,開窗結(jié)果為5
| 1 | 5 | 5 | <--前一行5、當(dāng)前行5、后一行NULL,比較而得的最大值,開窗結(jié)果為5
| 2 | 6 | 7 |
| 2 | 7 | 8 |
| 2 | 8 | 9 |
| 2 | 9 | 9 |
+------+------+------+
11 rows in set (0.00 sec)
再來試試使用range邏輯范圍,會產(chǎn)生什么奇妙的結(jié)果,這次我們使用sum函數(shù)
mysql> #使用range邏輯范圍
mysql> #使用1 preceding表示當(dāng)前行的前一行作為起點
mysql> #使用1 following表示當(dāng)前行的后一行作為終點
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、當(dāng)前行1、后一行2,1+2=3
| 1 | 2 | 9 | <--前一行1、當(dāng)前行2、后一行有2個相同的值,邏輯上規(guī)定為同一行的3,1+2+3+3=9
| 1 | 3 | 12 | <--前一行2、當(dāng)前行有2個相同的值,邏輯上規(guī)定為同一行的3、后一行4,2+3+3+4=12
| 1 | 3 | 12 | <--前一行2、當(dāng)前行有2個相同的值,邏輯上規(guī)定為同一行的3、后一行4,2+3+3+4=12
| 1 | 4 | 20 | <--前一行有2個相同的值,邏輯上規(guī)定為同一行的3、當(dāng)前行4、后一行有2個相同的值,邏輯上規(guī)定為同一行的5,3+3+4+5+5=20
| 1 | 5 | 14 | <--前一行4、當(dāng)前行有2個相同的值,邏輯上規(guī)定為同一行的5、后一行NULL,4+5+5=14
| 1 | 5 | 14 | <--前一行4、當(dāng)前行有2個相同的值,邏輯上規(guī)定為同一行的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