日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

《MySQL 8.0.22执行器源码分析(4.1)Item_sum类以及聚合》

發(fā)布時(shí)間:2023/12/1 数据库 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《MySQL 8.0.22执行器源码分析(4.1)Item_sum类以及聚合》 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Item_sum類用于SQL聚合函數(shù)的特殊表達(dá)式基類。

這些表達(dá)式是在聚合函數(shù)(sum、max)等幫助下形成的。item_sum類也是window函數(shù)的基類。
聚合函數(shù)(Aggregate Function)實(shí)現(xiàn)的大部分代碼在item_sum.h和item_sum.cc

聚合函數(shù)限制

不能在表達(dá)式的所有位置使用聚合函數(shù),使用聚合函數(shù)應(yīng)該有一些明確的限制。

在沒有嵌套的查詢中,select列表的表達(dá)式中,having自居中使用聚合函數(shù)是有效的,在where子句、form子句或group by子句則是無(wú)效的。

關(guān)于解釋,這篇文章可以看看:https://blog.csdn.net/weixin_33725515/article/details/85997761

在帶有嵌套子查詢的查詢中檢測(cè)聚合函數(shù)是否有效的規(guī)則比較復(fù)雜:

如下列查詢:

SELECT t1.a FROM t1 GROUP BY t1.a HAVING t1.a > ALL (SELECT t2.c FROM t2 WHERE SUM(t1.b) < t2.c).

在子查詢的where子句中使用了聚合函數(shù)sum(),但是由于它包含在外部查詢的having子句中,因此它是有效的。將針對(duì)主查詢中定義的每個(gè)組而不是子查詢的組來評(píng)估表達(dá)式sum。

又如下列查詢:

SELECT t1.a FROM t1 GROUP BY t1.a HAVING t1.a > ALL(SELECT t2.c FROM t2 GROUP BY t2.c HAVING SUM(t1.a) < t2.c)

聚合函數(shù)可以在外部查詢塊和內(nèi)部查詢塊中進(jìn)行評(píng)估,如果我們?yōu)橥獠坎樵冊(cè)u(píng)估sum,則將得到t1.a乘上t1組中的基數(shù)。在這種情況下,sum(t1.a)被用作每個(gè)相關(guān)子查詢中的常數(shù)。但是,也可以為內(nèi)部查詢?cè)u(píng)估sum。此時(shí)t1.a將是每個(gè)相關(guān)子查詢的常數(shù),并且對(duì)表t2的每個(gè)組執(zhí)行求和。

因此,根據(jù)向哪個(gè)查詢快分配聚合函數(shù),可以獲得不同的結(jié)果。

檢測(cè)聚合函數(shù)的查詢塊的一般規(guī)則如下:

考慮一個(gè)聚合函數(shù)S(E),其中E是一個(gè)包含列引用C1,…,Cn的表達(dá)式。針對(duì)包含聚合函數(shù)S(E)的查詢塊Qi會(huì)解析所有列引用Ci。令Q為所有查詢塊Qi中最內(nèi)部的查詢塊。(注意,S(E)絕對(duì)不能在包含子查詢Q的查詢的查詢塊聚合,否則S(E)將引用至少一個(gè)未綁定的列引用)。如果在允許聚合函數(shù)的Q的構(gòu)造中使用函數(shù)S(E),則我們?cè)赒中聚合S(E)。

否則:如果啟用了ANSI SQL模式,則報(bào)告錯(cuò)誤。如果沒有啟用,在允許使用S(E)的地方查找包含S(E)的最內(nèi)部的查詢塊。聚合的位置取決于子查詢包含在哪個(gè)子句中。當(dāng)包含在where子句中,包含在選擇列表中或包含在having子句時(shí),結(jié)果不同。

一些成員說明

成員base_select包含對(duì)其中包含了聚合函數(shù)的查詢塊的引用。

成員aggr_select包含對(duì)其中使用了聚合函數(shù)的查詢塊的引用。

max_aggr_level字段保留聚合函數(shù)中包含的未綁定列引用的最大嵌套級(jí)別。在嵌套級(jí)別較小的子查詢中不能包含聚合函數(shù)比max_aggr_level高。可以聚合在嵌套級(jí)別大于max_aggr_level的子查詢中。

如果聚合函數(shù)中不包含任何列引用,如count(*),則max_aggr_level為-1。

字段max_sum_func_level將包含用作給定聚合函數(shù)的參數(shù)的子表達(dá)式的聚合函數(shù)的嵌套級(jí)別的最大值,但不會(huì)在此聚合函數(shù)內(nèi)的任何子查詢塊中聚合。僅當(dāng)s1.max_sum_func_level < s0.max_sum_func_level時(shí),嵌套的聚合函數(shù)s1才能在聚合函數(shù)s0中使用。如果未在s0內(nèi)的任何子查詢中計(jì)算s1,則將聚合函數(shù)s1視為嵌套于集合函數(shù)s0。

當(dāng)我們使用遞歸方法fix_fields遍歷查詢子表達(dá)式時(shí),將檢查使用集合函數(shù)的條件。當(dāng)我們將此方法用于Item_sum類的對(duì)象時(shí),首先,在下降時(shí)(下降不知道指什么意思),調(diào)用init_sum_func_check方法,該方法初始化檢查時(shí)使用的成員。然后在上升過程中,調(diào)用方法check_sum_func,該方法驗(yàn)證設(shè)置的函數(shù)的用法,并在無(wú)效時(shí)報(bào)告錯(cuò)誤。方法check_sum_func用于鏈接在包含的查詢塊中聚合的聚合函數(shù)的item。此類函數(shù)的循環(huán)鏈通過字段inner_sum_func_list附加到相應(yīng)的select_lex結(jié)構(gòu)。

窗口函數(shù)

關(guān)于什么是窗口函數(shù),參考鏈接通俗易懂的學(xué)會(huì):SQL窗口函數(shù)

大部分的聚合函數(shù)如(sum、count、avg)也可以用作窗口函數(shù)。此時(shí)有如下限制:

1、不使用任何聚合器

2、不支持distinct

3、val_* ( ) 的作用不只是返回函數(shù)的當(dāng)前值:它首先將函數(shù)的參數(shù)累加到函數(shù)的狀態(tài)。例如處理end_write_wf()包含WF輸入的臨時(shí)表。每個(gè)輸入行都傳遞給copy_funcs(),后者調(diào)用WF的 val_ *()對(duì)其進(jìn)行累加。

類型判斷

很多時(shí)候我們要判斷聚合函數(shù)的類型,那么首先要先判斷當(dāng)前的操作是否是聚合操作,然后再判斷聚合操作的類型。

Item類有一個(gè)純虛函數(shù): virtual enum Type type() const =0; 其對(duì)應(yīng)的Type在該類中定義為:

enum Type {INVALID_ITEM = 0,FIELD_ITEM,FUNC_ITEM,SUM_FUNC_ITEM,STRING_ITEM,INT_ITEM,REAL_ITEM,NULL_ITEM,VARBIN_ITEM,COPY_STR_ITEM,FIELD_AVG_ITEM,DEFAULT_VALUE_ITEM,PROC_ITEM,COND_ITEM,REF_ITEM,FIELD_STD_ITEM,FIELD_VARIANCE_ITEM,INSERT_VALUE_ITEM,SUBSELECT_ITEM,ROW_ITEM,CACHE_ITEM,TYPE_HOLDER,PARAM_ITEM,TRIGGER_FIELD_ITEM,DECIMAL_ITEM,XPATH_NODESET,XPATH_NODESET_CMP,VIEW_FIXER_ITEM,FIELD_BIT_ITEM,VALUES_COLUMN_ITEM};

Item_sum從Item派生出來,除了type函數(shù)外,他還帶有一個(gè)函數(shù)virtual enum Sumfunctype sum_func () const=0;在Item_sum中定義了Sumfunctype的類型。

enum Sumfunctype {COUNT_FUNC, // COUNTCOUNT_DISTINCT_FUNC, // COUNT (DISTINCT)SUM_FUNC, // SUMSUM_DISTINCT_FUNC, // SUM (DISTINCT)AVG_FUNC, // AVGAVG_DISTINCT_FUNC, // AVG (DISTINCT)MIN_FUNC, // MINMAX_FUNC, // MAXSTD_FUNC, // STD/STDDEV/STDDEV_POPVARIANCE_FUNC, // VARIANCE/VAR_POP and VAR_SAMPSUM_BIT_FUNC, // BIT_AND, BIT_OR and BIT_XORUDF_SUM_FUNC, // user defined functionsGROUP_CONCAT_FUNC, // GROUP_CONCATJSON_AGG_FUNC, // JSON_ARRAYAGG and JSON_OBJECTAGGROW_NUMBER_FUNC, // Window functionsRANK_FUNC,DENSE_RANK_FUNC,CUME_DIST_FUNC,PERCENT_RANK_FUNC,NTILE_FUNC,LEAD_LAG_FUNC,FIRST_LAST_VALUE_FUNC,NTH_VALUE_FUNC,ROLLUP_SUM_SWITCHER_FUNC};

可以通過這些類型來判斷讀當(dāng)前具體的聚合操作。除此之外,還可以利用lex的一些helper函數(shù)來進(jìn)行輔助判斷,如is_single_grouped

bool is_single_grouped() const {return m_agg_func_used && group_list.elements == 0 &&m_having_cond == nullptr; }

如果該函數(shù)返回true,則說明沒有g(shù)roup by 也沒有have

主要的聚合函數(shù)具體在代碼中的類結(jié)構(gòu)和繼承關(guān)系


COUNT/SUM/AVG/STD/VAR_POP函數(shù):

MIN/MAX函數(shù):

BIT_OR/BIT_AND/BIT_XOR函數(shù):

聚合過程(不帶group by)

不帶group by的聚合會(huì)使用輔助類Aggregator,而group by并不使用該輔助類。

在優(yōu)化階段需要進(jìn)行setup,比如初始化distinct或者sorting需要臨時(shí)表或者臨時(shí)Tree結(jié)構(gòu),方便下階段的聚合。

JOIN::optimize--> JOIN::make_tmp_tables_info--> setup_sum_funcs--> Item_sum::aggregator_setup--> Aggregator_simple::setup--> Item_sum::setup-->

在執(zhí)行階段

AggregateIterator::Read()-> reset_and_add()-> aggregator_clear()aggregator_add()-> Aggregator::clear()、Aggregator::add()-> Item_sum_xxx::add()

在計(jì)算distinct聚合時(shí),還需要實(shí)現(xiàn)aggregator::endup(),因?yàn)閐istinct_aggregator::add()只是通過某種方式采集了unique的行,但是并未保存,需要在這個(gè)階段進(jìn)行保存。這個(gè)過程可以理解為在distinct的聚合過程中(add)無(wú)法判斷是否唯一。

注意group by場(chǎng)景下本身是通過臨時(shí)表解決唯一問題的。

聚合過程(帶group by)

MySQL對(duì)于帶GROUP BY的聚合,通常采用了Temp table的方式保存了(GROUP BY KEY, AGGR VALUE)
具體交給迭代器TemptableAggregateIterator實(shí)現(xiàn)。

TemptableAggregateIterator::Init()-> init_tmptable_sum_functions()update_tmptable_sum_func()-> Item_sum::reset_field()、Item_sum::update_field()->

Item_sum繼承于Item_result_field,意味著該類作為計(jì)算函數(shù)的同時(shí)也保存輸出結(jié)果。具體可以看每個(gè)Item_sum子類的val_xxx實(shí)現(xiàn),該函數(shù)負(fù)責(zé)對(duì)上層結(jié)果或者客戶端結(jié)果進(jìn)行輸出。

對(duì)于特殊聚合函數(shù)如AVG\STD\VAR_POP等函數(shù),在累加過程中,臨時(shí)保存的變量值有多個(gè),實(shí)際的輸出結(jié)果必須通過加工處理,尤其是在group by場(chǎng)景下,多個(gè)臨時(shí)變量需要保存到temp table中,下次累加時(shí)取出來,直到最終結(jié)果輸出。所以需要額外的輔助類Item_result_field,幫助該聚合函數(shù)進(jìn)行最終結(jié)果輸出。


舉例,對(duì)于Item_avg_field類的最終結(jié)果,需要通過Item_avg_field::val_xxx計(jì)算后進(jìn)行輸出。

調(diào)用順序如下:

ExecuteIteratorQuery()-> Query_result_send::send_data()-> THD::send_result_set_row()-> Item::send()-> Item_avg_field::val_xxx


小TIPS:如果內(nèi)核需要實(shí)現(xiàn)多線程并行計(jì)算聚合函數(shù)的時(shí)候,可以通過改造對(duì)中間結(jié)果輸出save_in_field_inner函數(shù),讓每個(gè)中間結(jié)果如按照設(shè)計(jì)保存到相應(yīng)的field->ptr中,保留到臨時(shí)表中。

一些函數(shù)(待補(bǔ)全)

Item_sum::check_sum_func

驗(yàn)證聚合函數(shù)的語(yǔ)義要求。檢查聚合函數(shù)的上下文是否允許對(duì)其進(jìn)行聚合,并且當(dāng)它是另一個(gè)聚合函數(shù)的參數(shù)時(shí),需要直接或間接確保該函數(shù)將這兩個(gè)聚合函數(shù)聚合在不同的查詢塊中。如果將聚合函數(shù)聚集在某個(gè)外部查詢塊中,則會(huì)將其添加到附加的聚集塊查詢item鏈inner_sum_func_list中。
tips:解析表達(dá)式時(shí),必須為所有的聚合函數(shù)調(diào)用此函數(shù),而且是以后綴順序調(diào)用。

Item_sum::cleanup

使用后調(diào)用每個(gè)item。作用是釋放所有分配的資源,例如動(dòng)態(tài)內(nèi)存。通過清除緩存的值為新的執(zhí)行做準(zhǔn)備。它不會(huì)刪除準(zhǔn)備期間分配的值,那些資源由析構(gòu)函數(shù)釋放。

參考

http://mysql.taobao.org/monthly/2019/05/02/
https://dev.mysql.com/doc/dev/mysql-server/latest/classItem__sum.html
MySQL 8.0.22源碼

總結(jié)

以上是生活随笔為你收集整理的《MySQL 8.0.22执行器源码分析(4.1)Item_sum类以及聚合》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。