javascript隨機變色例項程式碼
學習連線:https://blog.csdn.net/weixin_39010770/article/details/87862407
視窗
:記錄集合視窗函式
:在滿足某些條件的記錄集合上執行的特殊函式,對於每條記錄都要在此視窗內執行函式。有的函式隨著記錄的不同,視窗大小都是固定的,稱為靜態視窗
;有的函式則相反,不同的記錄對應著不同的視窗,稱為滑動視窗
。
1. 視窗函式和普通聚合函式的區別:
①聚合函式是將多條記錄聚合為一條;視窗函式是每條記錄都會執行,有幾條記錄執行完還是幾條。
②聚合函式也可以用於視窗函式。
2. 視窗函式的基本用法:
函式名 OVER 子句
over關鍵字
用來指定函式執行的視窗範圍,若後面括號中什麼都不寫,則意味著視窗包含滿足WHERE條件的所有行,視窗函式基於所有行進行計算;如果不為空,則支援以下4中語法來設定視窗。
①window_name:給視窗指定一個別名。如果SQL中涉及的視窗較多,採用別名可以看起來更清晰易讀;
②PARTITION BY 子句
③ORDER BY子句
:按照哪些欄位進行排序,視窗函式將按照排序後的記錄順序進行編號;
④FRAME子句
:FRAME
是當前分割槽的一個子集,子句用來定義子集的規則,通常用來作為滑動視窗使用。
# 先看一個例子 SELECT stu_id, score, sum(score) OVER (PARTITION BY stu_id) AS score_order FROM t_score; +--------+-------+-------------+ | stu_id | score | score_order |+--------+-------+-------------+ | 1 | 90 | 439 | | 1 | 95 | 439 | | 1 | 84 | 439 | | 1 | 75 | 439 | | 1 | 95 | 439 | | 2 | 88 | 420 | | 2 | 68 | 420 | | 2 | 98 | 420 | | 2| 88 | 420 | | 2 | 78 | 420 | | 3 | 90 | 427 | | 3 | 68 | 427 | | 3 | 85 | 427 | | 3 | 89 | 427 | | 3 | 95 | 427 | | 4 | 68 | 420 | | 4 | 87 | 420 | | 4 | 93 | 420 | | 4 | 87 | 420 | | 4 | 85 | 420 | | 5 | 92 | 360 | | 5 | 92 | 360 | | 5 | 91 | 360 | | 5 | 85 | 360 | +--------+-------+-------------+
3. 按功能劃分可將MySQL支援的視窗函式分為如下幾類:
①序號函式:ROW_NUMBER()
、RANK()
、DENSE_RANK()
- 用途:顯示分割槽中的當前行號
- 應用場景:查詢每個學生的分數最高的前3門課程
ROW_NUMBER() OVER (PARTITION BY stu_id ORDER BY score)
SELECT * FROM ( SELECT stu_id, ROW_NUMBER() OVER (PARTITION BY stu_id ORDER BY score DESC) AS score_order, lesson_id, score FROM t_score ) t WHERE score_order <= 3; +--------+-------------+-----------+-------+ | stu_id | score_order | lesson_id | score | +--------+-------------+-----------+-------+ | 1 | 1 | L002 | 95 | | 1 | 2 | L005 | 95 | | 1 | 3 | L001 | 90 | | 2 | 1 | L003 | 98 | | 2 | 2 | L001 | 88 | | 2 | 3 | L004 | 88 | | 3 | 1 | L005 | 95 | | 3 | 2 | L001 | 90 | | 3 | 3 | L004 | 89 | | 4 | 1 | L003 | 93 | | 4 | 2 | L002 | 87 | | 4 | 3 | L004 | 87 | | 5 | 1 | L001 | 92 | | 5 | 2 | L002 | 92 | | 5 | 3 | L003 | 91 | +--------+-------------+-----------+-------+
對於stu_id=1
的同學,有兩門課程的成績均為98,序號隨機排了1和2。但很多情況下二者應該是並列第一,則他的成績為88
的這門課的序號可能是第2名,也可能為第3名。
這時候,ROW_NUMBER()
就不能滿足需求,需要RANK()
和DENSE_RANK()
出場,它們和ROW_NUMBER()
非常類似,只是在出現重複值時處理邏輯有所不同。
ROW_NUMBER():順序排序——1、2、3 RANK():並列排序,跳過重複序號——1、1、3 DENSE_RANK():並列排序,不跳過重複序號——1、1、2
SELECT * FROM ( SELECT ROW_NUMBER() OVER (PARTITION BY stu_id ORDER BY score DESC) AS score_order1, RANK() OVER (PARTITION BY stu_id ORDER BY score DESC) AS score_order2, DENSE_RANK() OVER (PARTITION BY stu_id ORDER BY score DESC) AS score_order3, stu_id, lesson_id, score FROM t_score ) t WHERE stu_id = 1 AND score_order1 <= 3 AND score_order2 <= 3 AND score_order3 <= 3; +--------------+--------------+--------------+--------+-----------+-------+ | score_order1 | score_order2 | score_order3 | stu_id | lesson_id | score | +--------------+--------------+--------------+--------+-----------+-------+ | 1 | 1 | 1 | 1 | L002 | 95 | | 2 | 1 | 1 | 1 | L005 | 95 | | 3 | 3 | 2 | 1 | L001 | 90 | +--------------+--------------+--------------+--------+-----------+-------+
②分佈函式:PERCENT_RANK()
、CUME_DIST()
PERCENT_RANK()
- 用途:每行按照公式
(rank-1) / (rows-1)
進行計算。其中,rank
為RANK()函式
產生的序號,rows
為當前視窗的記錄總行數 - 應用場景:不常用
給視窗指定別名:WINDOW w AS (PARTITION BY stu_id ORDER BY score) rows = 5
SELECT RANK() OVER w AS rk, PERCENT_RANK() OVER w AS prk, stu_id, lesson_id, score FROM t_score WHERE stu_id = 1 WINDOW w AS (PARTITION BY stu_id ORDER BY score); +----+------+--------+-----------+-------+ | rk | prk | stu_id | lesson_id | score | +----+------+--------+-----------+-------+ | 1 | 0 | 1 | L004 | 75 | | 2 | 0.25 | 1 | L003 | 84 | | 3 | 0.5 | 1 | L001 | 90 | | 4 | 0.75 | 1 | L002 | 95 | | 4 | 0.75 | 1 | L005 | 95 | +----+------+--------+-----------+-------+
CUME_DIST()
- 用途:分組內小於、等於當前rank值的行數 / 分組內總行數
- 應用場景:查詢小於等於當前成績(score)的比例
cd1:沒有分割槽,則所有資料均為一組,總行數為8
cd2:按照 lesson_id 分成了兩組,行數各為4
SELECT stu_id, lesson_id, score, CUME_DIST() OVER (ORDER BY score) AS cd1 , CUME_DIST() OVER (PARTITION BY lesson_id ORDER BY score) AS cd2 FROM t_score WHERE lesson_id IN ('L001', 'L002'); +--------+-----------+-------+-----+-----+ | stu_id | lesson_id | score | cd1 | cd2 | +--------+-----------+-------+-----+-----+ | 4 | L001 | 68 | 0.3 | 0.2 | | 2 | L001 | 88 | 0.5 | 0.4 | | 1 | L001 | 90 | 0.7 | 0.8 | | 3 | L001 | 90 | 0.7 | 0.8 | | 5 | L001 | 92 | 0.9 | 1 | | 2 | L002 | 68 | 0.3 | 0.4 | | 3 | L002 | 68 | 0.3 | 0.4 | | 4 | L002 | 87 | 0.4 | 0.6 | | 5 | L002 | 92 | 0.9 | 0.8 | | 1 | L002 | 95 | 1 | 1 | +--------+-----------+-------+-----+-----+
③前後函式:LAG(expr,n)
、LEAD(expr,n)
- 用途:返回位於當前行的前n行(
LAG(expr,n)
)或後n行(LEAD(expr,n)
)的expr的值 - 應用場景:查詢前1名同學的成績和當前同學成績的差值
內層SQL先通過 LAG()函式 得到前1名同學的成績,外層SQL再將當前同學和前1名同學的成績做差得到成績差值 diff。
SELECT stu_id, lesson_id, score, pre_score , score - pre_score AS diff FROM ( SELECT stu_id, lesson_id, score , LAG(score, 1) OVER w AS pre_score FROM t_score WHERE lesson_id IN ('L001', 'L002') WINDOW w AS (PARTITION BY lesson_id ORDER BY score) ) t; +--------+-----------+-------+-----------+------+ | stu_id | lesson_id | score | pre_score | diff | +--------+-----------+-------+-----------+------+ | 4 | L001 | 68 | NULL | NULL | | 2 | L001 | 88 | 68 | 20 | | 1 | L001 | 90 | 88 | 2 | | 3 | L001 | 90 | 90 | 0 | | 5 | L001 | 92 | 90 | 2 | | 2 | L002 | 68 | NULL | NULL | | 3 | L002 | 68 | 68 | 0 | | 4 | L002 | 87 | 68 | 19 | | 5 | L002 | 92 | 87 | 5 | | 1 | L002 | 95 | 92 | 3 | +--------+-----------+-------+-----------+------+
④頭尾函式:FIRST_VALUE(expr)
、LAST_VALUE(expr)
- 用途:返回第一個(
FIRST_VALUE(expr)
)或最後一個(LAST_VALUE(expr)
)expr的值 - 應用場景:截止到當前成績,按照日期排序查詢第1個和最後1個同學的分數
新增新列:
mysql> ALTER TABLE t_score ADD create_time DATE;
SELECT stu_id, lesson_id, score, create_time , FIRST_VALUE(score) OVER w AS first_score, LAST_VALUE(score) OVER w AS last_score FROM t_score WHERE lesson_id IN ('L001', 'L002') WINDOW w AS (PARTITION BY lesson_id ORDER BY create_time); +--------+-----------+-------+-------------+-------------+------------+ | stu_id | lesson_id | score | create_time | first_score | last_score | +--------+-----------+-------+-------------+-------------+------------+ | 3 | L001 | 100 | 2018-08-07 | 100 | 100 | | 1 | L001 | 98 | 2018-08-08 | 100 | 98 | | 2 | L001 | 84 | 2018-08-09 | 100 | 99 | | 4 | L001 | 99 | 2018-08-09 | 100 | 99 | | 3 | L002 | 91 | 2018-08-07 | 91 | 91 | | 1 | L002 | 86 | 2018-08-08 | 91 | 86 | | 2 | L002 | 90 | 2018-08-09 | 91 | 90 | | 4 | L002 | 88 | 2018-08-10 | 91 | 88 | +--------+-----------+-------+-------------+-------------+------------+
⑤其它函式:NTH_VALUE(expr, n)
、NTILE(n)
NTH_VALUE(expr,n)
- 用途:返回視窗中第n個
expr
的值。expr
可以是表示式,也可以是列名 - 應用場景:截止到當前成績,顯示每個同學的成績中排名第2和第3的成績的分數
SELECT stu_id, lesson_id, score , NTH_VALUE(score, 2) OVER w AS second_score , NTH_VALUE(score, 3) OVER w AS third_score FROM t_score WHERE stu_id IN (1, 2) WINDOW w AS (PARTITION BY stu_id ORDER BY score); +--------+-----------+-------+--------------+-------------+ | stu_id | lesson_id | score | second_score | third_score | +--------+-----------+-------+--------------+-------------+ | 1 | L004 | 75 | NULL | NULL | | 1 | L003 | 84 | 84 | NULL | | 1 | L001 | 90 | 84 | 90 | | 1 | L002 | 95 | 84 | 90 | | 1 | L005 | 95 | 84 | 90 | | 2 | L002 | 68 | NULL | NULL | | 2 | L005 | 78 | 78 | NULL | | 2 | L001 | 88 | 78 | 88 | | 2 | L004 | 88 | 78 | 88 | | 2 | L003 | 98 | 78 | 88 | +--------+-----------+-------+--------------+-------------+
NTILE(n)
- 用途:將分割槽中的有序資料分為n個等級,記錄等級數
- 應用場景:將每門課程按照成績分成3組
SELECT NTILE(3) OVER w AS nf, stu_id, lesson_id, score FROM t_score WHERE lesson_id IN ('L001', 'L002') WINDOW w AS (PARTITION BY lesson_id ORDER BY score); +------+--------+-----------+-------+ | nf | stu_id | lesson_id | score | +------+--------+-----------+-------+ | 1 | 4 | L001 | 68 | | 1 | 2 | L001 | 88 | | 2 | 1 | L001 | 90 | | 2 | 3 | L001 | 90 | | 3 | 5 | L001 | 92 | | 1 | 2 | L002 | 68 | | 1 | 3 | L002 | 68 | | 2 | 4 | L002 | 87 | | 2 | 5 | L002 | 92 | | 3 | 1 | L002 | 95 | +------+--------+-----------+-------+
NTILE(n)函式
在資料分析中應用較多,比如由於資料量大,需要將資料平均分配到n個並行的程序分別計算,此時就可以用NTILE(n)
對資料進行分組(由於記錄數不一定被n整除,所以資料不一定完全平均),然後將不同桶號的資料再分配。
4. 聚合函式作為視窗函式:
- 用途:在視窗中每條記錄動態地應用聚合函式(
SUM()
、AVG()
、MAX()
、MIN()
、COUNT()
),可以動態計算在指定的視窗內的各種聚合函式值 - 應用場景:截止到當前時間,查詢
stu_id=1
的學生的累計分數、分數最高的科目、分數最低的科目
SELECT stu_id, lesson_id, score, create_time , FIRST_VALUE(score) OVER w AS first_score
, LAST_VALUE(score) OVER w AS last_score FROM t_score WHERE lesson_id IN ('L001', 'L002') WINDOW w AS (PARTITION BY lesson_id ORDER BY create_time); +--------+-----------+-------+-------------+-----------+-----------+-----------+ | stu_id | lesson_id | score | create_time | score_sum | score_max | score_min | +--------+-----------+-------+-------------+-----------+-----------+-----------+ | 1 | L001 | 98 | 2018-08-08 | 184 | 98 | 86 | | 1 | L002 | 86 | 2018-08-08 | 184 | 98 | 86 | | 1 | L003 | 79 | 2018-08-09 | 263 | 98 | 79 | | 1 | L004 | 88 | 2018-08-10 | 449 | 98 | 79 | | 1 | L005 | 98 | 2018-08-10 | 449 | 98 | 79 | +--------+-----------+-------+-------------+-----------+-----------+-----------+