Mysql8.0的新特點:with聲明, 窗口函數


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分組。✅

 

圖例子:

 

 

明白了這些生詞的意思,然后看上面的例子就能理解了。

更多用法還要看文檔(上面第一個連接)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM