主頁 > 資料庫 > MySQL 視窗函式

MySQL 視窗函式

2022-10-09 08:18:40 資料庫

1. 視窗函式概念和語法

視窗函式對一組查詢行執行類似聚合的操作,然而,聚合操作將查詢行分組到單個結果行,而視窗函式為每個查詢行產生一個結果:

  • 函式求值發生的行稱為當前行
  • 與發生函式求值的當前行相關的查詢行組成了當前行的視窗

相比之下,視窗操作不會將一組查詢行折疊到單個輸出行,相反,它們為每一行生成一個結果,

SELECT 
    manufacturer, product, profit,
    SUM(profit) OVER() AS total_profit,
    SUM(profit) OVER(PARTITION BY manufacturer) AS manufacturer_profit
FROM sales;

查詢中的每個視窗操作都通過包含一個 OVER 子句來表示,該子句指定如何將查詢行劃分為組以供視窗函式處理:

  • 第一個 OVER 子句是空的,它將整個查詢行集視為一個磁區,視窗函式因此產生一個全域和,但對每一行都這樣做,
  • 第二個 OVER 子句按 manufacturer 劃分行,產生每個磁區(每個manufacturer)的總和,該函式為每個磁區行生成此總和,

視窗函式只允許在查詢串列和 ORDER BY 子句中使用,

查詢結果行由 FROM 子句確定,在 WHEREGROUP BYHAVING 處理之后,視窗執行發生在 ORDER BYLIMITSELECT DISTINCT 之前,

OVER子句被允許用于許多聚合函式,因此,這些聚合函式可以用作視窗函式或非視窗函式,具體取決于是否存在 OVER 子句:

AVG()
BIT_AND()
BIT_OR()
BIT_XOR()
COUNT()
JSON_ARRAYAGG()
JSON_OBJECTAGG()
MAX()
MIN()
STDDEV_POP(), STDDEV(), STD()
STDDEV_SAMP()
SUM()
VAR_POP(), VARIANCE()
VAR_SAMP()

MySQL還支持只能作為視窗函式使用的非聚合函式,對于這些,OVER子句是必須的

CUME_DIST()
DENSE_RANK()
FIRST_VALUE()
LAG()
LAST_VALUE()
LEAD()
NTH_VALUE()
NTILE()
PERCENT_RANK()
RANK()
ROW_NUMBER()

ROW_NUMBER() 它生成其磁區內每一行的行號,默認情況下,磁區行是無序的,行編號是不確定的,若要對磁區行進行排序,請在視窗定義中包含一個ORDER BY子句,下面的示例中,查詢使用無序磁區和有序磁區(row_num1和row_num2列)來說明省略和包含ORDER BY之間的區別:

SELECT 
    manufacturer, product, profit,
    ROW_NUMBER() OVER(PARTITION BY manufacturer) AS row_num1,
    ROW_NUMBER() OVER(PARTITION BY manufacturer ORDER BY profit) AS row_num2
FROM sales;

如前所述,要使用視窗函式(或將聚合函式視為視窗函式),需要在函式呼叫后包含OVER子句,OVER子句有兩種形式:

over_clause:
    {OVER (window_spec) | OVER window_name}

這兩種形式都定義了視窗函式應該如何處理查詢行,它們的區別在于視窗是直接在OVER子句中定義的,還是通過對查詢中其他地方定義的命名視窗的參考提供的:

  • 在第一種情況下,視窗規范直接出現在 OVER 子句中的括號之間,
  • 在第二種情況下,window_name 是由查詢中其他地方的 WINDOW 子句定義的視窗規范的名稱,

對于 OVER (window_spec) 語法,視窗規范有幾個部分,都是可選的:

window_spec:
    [window_name] [partition_clause] [order_clause] [frame_clause]

如果 OVER() 為空,則視窗由所有查詢行組成,視窗函式使用所有行計算結果,否則,括號中的子句決定了使用哪些查詢行來計算函式結果,以及它們是如何磁區和排序的:

  • window_name: 由查詢中其他地方的window子句定義的視窗的名稱,如果window_name單獨出現在OVER子句中,則它完全定義了視窗,如果磁區、排序或分幀子句也給出了,它們會修改被命名視窗的解釋,
  • partition_clause: PARTITION BY 子句指示如何將查詢行分組,給定行的視窗函式結果基于包含該行的磁區的行,如果省略 PARTITION BY,則有一個由所有查詢行組成的磁區,
    partition_clause:
        PARTITION BY expr [, expr] ...
  • order_clause: ORDER BY 子句指示如何對每個磁區中的行進行排序,根據 ORDER BY 子句相等的磁區行被視為對等,如果省略 ORDER BY,則磁區行是無序的,沒有隱含的處理順序,并且所有磁區行都是對等的,
    order_clause:
        ORDER BY expr [ASC|DESC] [, expr [ASC|DESC]] ...

每個ORDER BY運算式后面可以有選擇地跟著ASC或DESC來表示排序方向,NULL 值首先進行升序排序,最后進行降序排序,

視窗定義中的 ORDER BY 適用于各個磁區,要將結果集作為一個整體進行排序,請在查詢頂層包含 ORDER BY, 

  • frame_clause: frame是當前磁區的子集,frame子句指定如何定義該子集,

小結:

視窗,就是資料范圍,也可以理解為記錄集合,視窗函式就是在滿足某種條件的記錄集合上執行的特殊函式,即,應用在視窗內的函式,

  • 靜態視窗:視窗大小是固定的,視窗內的每條記錄都要執行此函式
  • 動態視窗:也叫滑動視窗,視窗大小是變化的

視窗函式有以下功能:

  • 同時具有分組和排序的功能
  • 不減少原表的行數

2. 視窗函式frame規范

一個frame是當前磁區的一個子集,frame子句指定如何定義這個子集,

frame是根據當前行確定的,這使得frame可以根據當前行在磁區中的位置在磁區中移動,

  • 通過將一個frame定義為從磁區開始到當前行的所有行,我們可以計算每一行的運行總數,
  • 通過將一個frame定義為在當前行的每一邊擴展N行,我們可以計算滾動平均,

下面的查詢演示了如何使用移動幀來計算每組按時間順序排列的值的總和,以及從當前行和緊隨其后的行計算的滾動平均值:

SELECT 
  manufacturer, `month`, profit, 
  SUM(profit) OVER(
	    PARTITION BY manufacturer 
	    ORDER BY `month` 
	    ROWS unbounded PRECEDING
	  ) AS running_total, 
  AVG(profit) OVER(
	    PARTITION BY manufacturer 
	    ORDER BY `month` 
	    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
	  ) AS running_average 
FROM 
  sales;

frame 子句語法:

frame_clause:
    frame_units frame_extent

frame_units:
    {ROWS | RANGE}

在沒有frame子句的情況下,默認frame取決于是否存在ORDER BY子句,

frame_units值表示當前行和幀行之間的關系型別:

  • ROWS: frame由開始行和結束行位置定義,偏移量是行號與當前行號之間的差異,
  • RANGE: frame由值范圍內的行定義,偏移量是行值與當前行值之間的差異,

frame_extend 表示frame的起始點和結束點,可以只指定frame的開始(在這種情況下,當前行隱式地是結束)或使用BETWEEN指定frame的兩個端點:

frame_extent:
    {frame_start | frame_between}

frame_between:
    BETWEEN frame_start AND frame_end

frame_start, frame_end: {
    CURRENT ROW
  | UNBOUNDED PRECEDING
  | UNBOUNDED FOLLOWING
  | expr PRECEDING
  | expr FOLLOWING
}

使用BETWEEN語法,frame_start不能發生在frame_end之后,

允許的frame_start和frame_end值含義如下:

  • CURRENT ROW:  對于ROWS,邊界是當前行,對于RANGE,邊界是當前行的對等點,
  • UNBOUNDED PRECEDING:  邊界是第一個磁區行,
  • UNBOUNDED FOLLOWING:  邊界是最后一個磁區行,
  • expr PRECEDING:  對于ROWS,邊界是當前行之前的 expr 行,對于RANGE,邊界是值等于當前行值減去 expr 的行,如果當前行值為NULL,則系結為該行的對等體,
  • expr FOLLOWING:  對于ROWS,邊界是當前行之后的 expr 行,對于RANGE,邊界是值等于當前行值加上 expr 的行,如果當前行值為NULL,則系結為該行的對等體,

下面是一些有效expr PRECEDINGexpr FOLLOWING 示例:

10 PRECEDING
INTERVAL 5 DAY PRECEDING
5 FOLLOWING
INTERVAL '2:30' MINUTE_SECOND FOLLOWING

在沒有frame子句的情況下,默認的frame取決于是否存在ORDER BY子句:

  • ORDER BY:默認frame包括從磁區開始到當前行的行,包括當前行的所有對等點,與之等效的frame如下:
    RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  • 沒有ORDER BY:默認frame包括所有的磁區行(因為,如果沒有ORDER BY,所有的磁區行都是對等的),與之等效的frame如下:
    RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING

因為默認frame會根據是否存在ORDER BY而有所不同,所以向查詢添加ORDER BY以獲得確定性結果可能會更改結果,要獲得相同的結果,但按ORDER BY排序,無論ORDER BY是否存在,都要提供要使用的顯式frame規范, 

3. 視窗函式應用

示例資料

序號函式

select
	name, subject, score,
	rank() over w as 'rank',
	dense_rank() over w as 'dense_rank',
	row_number() over w as 'row_number'
from
	student 
window w as (partition by subject order by score desc);

可以看到,row_number就是個序號,rank在處理并列情況的時候會占用后面的序號,而dense_rank不會

同時,這個SQL中使用了命名視窗寫法

Top-N問題:每個類別中取前N條 

這類問題可以套用這個模板

SELECT * FROM (SELECT *,row_number() over (PARTITION BY 姓名 ORDER BY 成績 DESC) AS ranking FROM test) AS tmp WHERE tmp.ranking <= N;

查詢每科第一名

select * from ( 
	select
		name, subject, score,
		dense_rank() over(partition by subject order by score desc) as 'rn'
	from
		student 
) tmp where tmp.rn = 1;

每科前三名

select * from ( 
	select
		name,
		subject,
		score,
		row_number() over(partition by subject order by score desc) as 'rn'
	from
		student 
) tmp where tmp.rn <= 3;

每科高于平均分數(寫法一)

select * from (
	select
		name, subject, score,
		avg(score) over(partition by subject) as 'avg_score'
	from
		student
) tmp where tmp.score > tmp.avg_score;

高于每科平均分數(寫法二) 

select
	name, subject, score
from
	student s
where
	s.score > (select avg(score) from student s2 where s2.subject = s.subject) 
order by s.subject asc;

聚集函式作視窗函式

select
	name, subject, score,
	first_value(score) over(partition by subject order by score desc) as '單科最高分',
	max(score) over(partition by subject) as '科目最高分',
	min(score) over(partition by subject) as '科目最低分',
	avg(score) over(partition by subject) as '科目平均分',
	sum(score) over(partition by subject order by score desc rows between unbounded preceding and current row) as '總分',
	sum(score) over(partition by name) as '學生總分',
	count(subject) over (partition by name) as '參加的學科數'
from
	student order by subject;

假設90分算及格,求每個學生的及格率

select
	t1.name,
	t1.pass_num as '通過的科目數',
	t2.total_num as '參加的科目數',
	concat(round((t1.pass_num / t2.total_num) * 100, 2), '%') as '及格率'
from
	(select name, count(*) pass_num from student where score > 90 group by name) t1
left join (select name, count(*) total_num from student group by name) t2 
	on t1.name = t2.name;

最后,視窗函式只能在查詢或子查詢中使用,不能在UPDATE或DELETE陳述句中使用它們來更新行,

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/511104.html

標籤:其他

上一篇:大資料架構-Spark-configuration(官網檔案)(學習隨筆)

下一篇:day08-MySQL事務

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more