MySQL中的開窗函數(shù)
前言 窗口函數(shù)的格式 函數(shù)(Function)的類型 開窗函數(shù)over() 窗口函數(shù)使用 ROW_NUMBER() RANK()與DENSE_RANK() LEAD()與LAG() FIRST_VALUE()與LAST_VALUE() NTILE() MAX()、MIN()、AVG()、SUM()與COUNT() 窗口從句的使用 窗口從句進(jìn)階
前言
窗口函數(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進(jìn)行分組,一般是表中的某一個字段,所以可以把partition by 看作與GROUP BY 具有相同功能的語法。 order by order_by_clause:即排序,通過order_by_clause 進(jìn)行排序,一般是在分組(partition by)之后再進(jìn)行排序,如此一來,就是在組內(nèi)進(jìn)行排序。如果沒有前面的分組子句(partition by),那么就是全部數(shù)據(jù)進(jìn)行排序。和普通MySQL中的查詢語句一樣,排序從句也支持ASC和DESC的用法。 Window_clause:窗口從句,它是排序之后的功能擴(kuò)展,它標(biāo)識了在排序之后的一個范圍,它的格式是: 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:指明窗口開始于分組的第一行,以排序之后的第一行為起點(diǎn); current row:以當(dāng)前行為起點(diǎn); n preceding:以當(dāng)前行的前面第n行為起點(diǎn); n following:以當(dāng)前行的后面第n行為起點(diǎn);
unbounded following:以排序之后的最后一行為終點(diǎn); current row:以當(dāng)前行為終點(diǎn); n preceding:以當(dāng)前行的前面第n行為終點(diǎn); n following:以當(dāng)前行的后面第n行為終點(diǎ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進(jìn)行分組,同一個組內(nèi)的數(shù)據(jù)再根據(jù)sale進(jìn)行排序,這個排序序號是唯一并且連續(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ìn)行分組,分組后根據(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ìn)行分組,分組后根據(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ù)查詢到的順序進(jìn)行排名;而rank函數(shù)和dense_rank函數(shù)對相同的數(shù)據(jù)會有一個相同的次序; rank函數(shù)的排序是可能不連續(xù)的,dense_rank函數(shù)的排序是連續(xù)的
lead(EXPR,,) over(partition by col1 order?by col2)
lag(EXPR,,) over(partition by col1 order?by col2) 其中:
EXPR通常是直接是列名,也可以是從其他行返回的表達(dá)式; OFFSET是默認(rèn)為1,表示在當(dāng)前分區(qū)內(nèi)基于當(dāng)前行的偏移行數(shù); DEFAULT是在OFFSET指定的偏移行數(shù)超出了分組的范圍時(因為默認(rèn)會返回null),可以通過設(shè)置這個字段來返回一個默認(rèn)值來替代null。
mysql> #為每一行數(shù)據(jù)的下一行數(shù)據(jù)進(jìn)行開窗,如果該行沒有下一行數(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ù)進(jìn)行開窗,如果該行沒有上一行數(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是默認(rèn)的
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進(jìn)行分組,同一個組內(nèi)的數(shù)據(jù)再根據(jù)sale進(jìn)行排序,查看每一個分組的第一個值
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進(jìn)行分組,同一個組內(nèi)的數(shù)據(jù)再根據(jù)sale進(jìn)行排序,查看每一個分組的最后一個值
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)其實(shí),在上面這個代碼中,完整的顯示是這樣的:
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 rowrows between unbounded preceding and?current rowmysql> 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 followingmysql> #分組之后沒有排序,就沒有默認(rèn)的窗口子句,得到的結(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> #排序如果沒有窗口子句會有一個默認(rèn)的規(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)前行的前一行作為起點(diǎn)
mysql> #使用1 following表示當(dāng)前行的后一行作為終點(diǎn)
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)前行的前一行作為起點(diǎn)
mysql> #使用1 following表示當(dāng)前行的后一行作為終點(diǎn)
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)最后給大家分享我寫的SQL兩件套:《SQL基礎(chǔ)知識第二版》和《SQL高級知識第二版》的PDF電子版。里面有各個語法的解釋、大量的實(shí)例講解和批注等等,非常通俗易懂,方便大家跟著一起來實(shí)操。
有需要的讀者可以下載學(xué)習(xí),在下面的公眾號「數(shù)據(jù)前線」(非本號)后臺回復(fù)關(guān)鍵字:SQL,就行
數(shù)據(jù)前線 ——End——
后臺回復(fù)關(guān)鍵字:1024,獲取一份精心整理的技術(shù)干貨
后臺回復(fù)關(guān)鍵字:進(jìn)群,帶你進(jìn)入高手如云的交流群。
推薦閱讀
評論
圖片
表情


