Mysql8.0的新特點
1-6號更新:窗口函數的frame_clause的使用
with聲明
https://dev.mysql.com/doc/refman/8.0/en/with.html
也叫做common table expression。(CTE)
CTE是一個命名的臨時結果集合,用在一個聲明的內部,可以被多次反復使用。非常類似sql5.7的衍生表(derived table)
WITH cte AS (SELECT 1) SELECT * FROM cte; SELECT * FROM (SELECT 1) AS dt;
本文只是簡單的介紹一下。
CTE的用途就是優化查詢sql的方法。見這篇文章:Section 8.2.2.4, “Optimizing Derived Tables, View References, and Common Table Expressions with Merging or Materialization”.
Common Table Expressions
使用with子句和1個或幾個逗號分隔的子句。每個子句提供一個子查詢,子查詢產生一個結果集合,給這個集合設置一個名字。例子:
mysql> with -> sc_60 as (select * from SC where score >= 60) -> select * from sc_60; +------+------+-------+ | SId | CId | score | +------+------+-------+ | 01 | 01 | 80.0 | | 01 | 02 | 90.0 | | 01 | 03 | 99.0 | | 02 | 01 | 70.0 | | 02 | 02 | 60.0 | | 02 | 03 | 80.0 | | 03 | 01 | 80.0 | | 03 | 02 | 80.0 | | 03 | 03 | 80.0 | | 05 | 01 | 76.0 | | 05 | 02 | 87.0 | | 07 | 02 | 89.0 | | 07 | 03 | 98.0 | +------+------+-------+ 13 rows in set (0.00 sec)
區別和優勢
- CTE和衍生表都需要命名
- 兩者都是為一個單獨的聲明而出現。
- 但,衍生表在一個查詢內部只能被引用一次,
- 而,CTE可以被多次引用。
- 並,CTE可以自我引用。用於遞歸recursive。
- 並,CTE可讀性更強,它出現在聲明的最開始位置,而不是被嵌套在語句內部。
窗口函數 windows function (非聚合窗口函數)官方文檔
MySQL 8.0窗口函數:用非常規思維簡易實現SQL需求(中文)
需要在單表中滿足某些條件的記錄集內部做一些函數操作,不是簡單的表連接,也不是簡單的聚合可以實現的,通常會讓寫SQL的同學焦頭爛額、絞盡腦汁,費了大半天時間寫出來一堆長長的晦澀難懂的自連接SQL,且性能低下,難以維護。
要解決此類問題,最方便的就是使用窗口函數。
- 本章描述了非聚合窗口函數,因為每一行來自一個查詢,使用和這個行相關的行來執行一個計算。
- 大多數聚合函數可以被用作窗口函數。
幾個函數的表格:
Name | Description |
dense_rank() | Rank of current row within its partition, without gaps |
rank() | Rank of current row within its partition, with gaps |
row_number() | 每行加一個行號 |
rank()和dense_rank()的區別https://www.cnblogs.com/chentianwei/p/12128269.html
窗口函數的概念和語法
https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html
傳統分組聚合函數和窗口函數的區別
- 傳統group by配合聚合函數,是把查詢的row導入一個單一的結果行row
- 窗口函數不是這樣,它針對每一個查詢row產生一個結果。
概念:
- 當前行: 函數計算所在行被稱為當前行current row
- 當前行的窗口: 當前行涉及的使用函數計算的query rows組成了一個窗口。所以窗口就是指當前行涉及的使用函數計算的query rows。
格式:
函數() OVER ([PARTITION BY expr,..], [order by expr [asc|desc],...]) AS 別名
- over()內部有3塊分別是partition by , order by , 和最后一個frame子句。
- ⚠️如果over()內什么都不寫,則函數作用於where子句的范圍,即基於所有行計算。
(1-6號更新)
窗口函數中的Frame子句的使用
參考https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html (看案例代碼)
參考https://www.mysqltutorial.org/mysql-window-functions/ (看文章最后的圖的說明)
這塊知識點需要看案例來理解:
mysql> SELECT time, subject, val, SUM(val) OVER (PARTITION BY subject ORDER BY time ROWS UNBOUNDED PRECEDING) AS running_total, AVG(val) OVER (PARTITION BY subject ORDER BY time ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS running_average FROM observations; +----------+---------+------+---------------+-----------------+ | time | subject | val | running_total | running_average | +----------+---------+------+---------------+-----------------+ | 07:00:00 | st113 | 10 | 10 | 9.5000 | | 07:15:00 | st113 | 9 | 19 | 14.6667 | | 07:30:00 | st113 | 25 | 44 | 18.0000 | | 07:45:00 | st113 | 20 | 64 | 22.5000 | | 07:00:00 | xh458 | 0 | 0 | 5.0000 | | 07:15:00 | xh458 | 10 | 10 | 5.0000 | | 07:30:00 | xh458 | 5 | 15 | 15.0000 | | 07:45:00 | xh458 | 30 | 45 | 20.0000 | | 08:00:00 | xh458 | 25 | 70 | 27.5000 | +----------+---------+------+---------------+-----------------+
這是使用窗口函數得到的一個表格。
2020-03-05補:
上面的一句如今我都看不懂了。現在的理解是,frame就是以當前行為基,取上下行的范圍區間。這個區間的值被傳入聚合函數,進行運算。
2020-4-12補充:
把上面的數據輸入數據庫,進行測驗:
1. over()內,只有一個partition by參數。
select *, sum(val) over(partition by subject) as sum_val from observations;
07:45:00 st113 20 64
07:30:00 st113 25 64
07:15:00 st113 9 64
07:00:00 st113 10 64
08:00:00 xh458 25 70
07:45:00 xh458 30 70
07:30:00 xh458 5 70
07:15:00 xh458 10 70
07:00:00 xh458 0 70
結果,窗口函數作用於整個分區。
即使用:ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
2.over()內,有一個partition by參數和一個order by 參數。
select *, sum(val) over(partition by subject order by time) as sum_val from observations; 07:00:00 st113 10 10 07:15:00 st113 9 19 07:30:00 st113 25 44 07:45:00 st113 20 64 07:00:00 xh458 0 0 07:15:00 xh458 10 10 07:30:00 xh458 5 15 07:45:00 xh458 30 45 08:00:00 xh458 25 70
結果默認使用:RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
3. 改變數據, over()內,有2個partition by參數
07:00:00,st113,10, 07:15:00,st113,10, 07:15:00,st113,25, 07:45:00,st113,20, 07:00:00,xh458,0, 07:15:00,xh458,10, 07:30:00,xh458,5, 07:45:00,xh458,30, 08:00:00,xh458,25, 07:15:00,st113,100,
select *, sum(val) over(partition by subject, time ) as sum_val from observations;
結果:窗口函數作用於整個分組。即subject + time的分組。
⚠️更復雜的2個partition by,2個order by,還是根據目的加上frame明確范圍的好
概念解釋
frame子句的抽象格式:
frame_unit {<frame_start>|<frame_between>}
frame_unit有rows和range兩種。
frame_start有3種:
UNBOUNDED PRECEDING
: 從partition的開始到當前行current row的范圍N PRECEDING
: a physical N of rows before the first current row. 當前行,向上數N行的范圍。N可以是數值或經過表達式計算返回的數。CURRENT ROW
: 即當前行
frame_between: 抽象表示:BETWEEN frame_boundary_1 AND frame_boundary_2
frame_boundary_1
和frame_boundary_2
可以是:
frame_start
UNBOUNDED FOLLOWING
: 從當前行到partition的最后一行的范圍。N FOLLOWING
: 當前行,向下數N行的范圍。
⚠️如果over()內使用不指定Frame子句,SQL默認使用:
⚠️👆這題規則👇只針對部分窗口函數生效。
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2020-03-06補充:
發現了rows和range的區別:
- 用rows參數,則不考慮重復的值。
- 用range參數,如果當前行的值是10,相鄰下一行的值也是10,sum()計算時,會把相鄰的這兩個值相加的。即考慮重復。具體見博客:
frame子句的對什么函數有影響?
- 部分聚合函數
- first_value, last_value,nth_value 3個窗口函數。
- 不會影響如rank, row_number等窗口函數,即使加上frame子句,這些窗口函數的作用范圍仍然是整個partition。
- 例子:sum() over(partition by xxx) 作用於整個partition分組。✅
圖例子:
明白了這些生詞的意思,然后看上面的例子就能理解了。
更多用法還要看文檔(上面第一個連接)