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

Item_sum类用于SQL聚合函数的特殊表达式基类。

这些表达式是在聚合函数(sum、max)等帮助下形成的。item_sum类也是window函数的基类。
聚合函数(Aggregate Function)实现的大部分代码在item_sum.h和item_sum.cc

聚合函数限制

不能在表达式的所有位置使用聚合函数,使用聚合函数应该有一些明确的限制。

在没有嵌套的查询中,select列表的表达式中,having自居中使用聚合函数是有效的,在where子句、form子句或group by子句则是无效的。

关于解释,这篇文章可以看看:https://blog.csdn.net/weixin_33725515/article/details/85997761

在带有嵌套子查询的查询中检测聚合函数是否有效的规则比较复杂:

如下列查询:

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子句中使用了聚合函数sum(),但是由于它包含在外部查询的having子句中,因此它是有效的。将针对主查询中定义的每个组而不是子查询的组来评估表达式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)

聚合函数可以在外部查询块和内部查询块中进行评估,如果我们为外部查询评估sum,则将得到t1.a乘上t1组中的基数。在这种情况下,sum(t1.a)被用作每个相关子查询中的常数。但是,也可以为内部查询评估sum。此时t1.a将是每个相关子查询的常数,并且对表t2的每个组执行求和。

因此,根据向哪个查询快分配聚合函数,可以获得不同的结果。

检测聚合函数的查询块的一般规则如下:

考虑一个聚合函数S(E),其中E是一个包含列引用C1,…,Cn的表达式。针对包含聚合函数S(E)的查询块Qi会解析所有列引用Ci。令Q为所有查询块Qi中最内部的查询块。(注意,S(E)绝对不能在包含子查询Q的查询的查询块聚合,否则S(E)将引用至少一个未绑定的列引用)。如果在允许聚合函数的Q的构造中使用函数S(E),则我们在Q中聚合S(E)。

否则:如果启用了ANSI SQL模式,则报告错误。如果没有启用,在允许使用S(E)的地方查找包含S(E)的最内部的查询块。聚合的位置取决于子查询包含在哪个子句中。当包含在where子句中,包含在选择列表中或包含在having子句时,结果不同。

一些成员说明

成员base_select包含对其中包含了聚合函数的查询块的引用。

成员aggr_select包含对其中使用了聚合函数的查询块的引用。

max_aggr_level字段保留聚合函数中包含的未绑定列引用的最大嵌套级别。在嵌套级别较小的子查询中不能包含聚合函数比max_aggr_level高。可以聚合在嵌套级别大于max_aggr_level的子查询中。

如果聚合函数中不包含任何列引用,如count(*),则max_aggr_level为-1。

字段max_sum_func_level将包含用作给定聚合函数的参数的子表达式的聚合函数的嵌套级别的最大值,但不会在此聚合函数内的任何子查询块中聚合。仅当s1.max_sum_func_level < s0.max_sum_func_level时,嵌套的聚合函数s1才能在聚合函数s0中使用。如果未在s0内的任何子查询中计算s1,则将聚合函数s1视为嵌套于集合函数s0。

当我们使用递归方法fix_fields遍历查询子表达式时,将检查使用集合函数的条件。当我们将此方法用于Item_sum类的对象时,首先,在下降时(下降不知道指什么意思),调用init_sum_func_check方法,该方法初始化检查时使用的成员。然后在上升过程中,调用方法check_sum_func,该方法验证设置的函数的用法,并在无效时报告错误。方法check_sum_func用于链接在包含的查询块中聚合的聚合函数的item。此类函数的循环链通过字段inner_sum_func_list附加到相应的select_lex结构。

窗口函数

关于什么是窗口函数,参考链接通俗易懂的学会:SQL窗口函数

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

1、不使用任何聚合器

2、不支持distinct

3、val_* ( ) 的作用不只是返回函数的当前值:它首先将函数的参数累加到函数的状态。例如处理end_write_wf()包含WF输入的临时表。每个输入行都传递给copy_funcs(),后者调用WF的 val_ *()对其进行累加。

类型判断

很多时候我们要判断聚合函数的类型,那么首先要先判断当前的操作是否是聚合操作,然后再判断聚合操作的类型。

Item类有一个纯虚函数: virtual enum Type type() const =0; 其对应的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函数外,他还带有一个函数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};

可以通过这些类型来判断读当前具体的聚合操作。除此之外,还可以利用lex的一些helper函数来进行辅助判断,如is_single_grouped

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

如果该函数返回true,则说明没有group by 也没有have

主要的聚合函数具体在代码中的类结构和继承关系

在这里插入图片描述
COUNT/SUM/AVG/STD/VAR_POP函数:
在这里插入图片描述
MIN/MAX函数:
在这里插入图片描述
BIT_OR/BIT_AND/BIT_XOR函数:
在这里插入图片描述

聚合过程(不带group by)

不带group by的聚合会使用辅助类Aggregator,而group by并不使用该辅助类。
在这里插入图片描述

在优化阶段需要进行setup,比如初始化distinct或者sorting需要临时表或者临时Tree结构,方便下阶段的聚合。

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

在执行阶段

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

在计算distinct聚合时,还需要实现aggregator::endup(),因为distinct_aggregator::add()只是通过某种方式采集了unique的行,但是并未保存,需要在这个阶段进行保存。这个过程可以理解为在distinct的聚合过程中(add)无法判断是否唯一。

注意group by场景下本身是通过临时表解决唯一问题的。

聚合过程(带group by)

MySQL对于带GROUP BY的聚合,通常采用了Temp table的方式保存了(GROUP BY KEY, AGGR VALUE)
具体交给迭代器TemptableAggregateIterator实现。

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

Item_sum继承于Item_result_field,意味着该类作为计算函数的同时也保存输出结果。具体可以看每个Item_sum子类的val_xxx实现,该函数负责对上层结果或者客户端结果进行输出。

对于特殊聚合函数如AVG\STD\VAR_POP等函数,在累加过程中,临时保存的变量值有多个,实际的输出结果必须通过加工处理,尤其是在group by场景下,多个临时变量需要保存到temp table中,下次累加时取出来,直到最终结果输出。所以需要额外的辅助类Item_result_field,帮助该聚合函数进行最终结果输出。

在这里插入图片描述
举例,对于Item_avg_field类的最终结果,需要通过Item_avg_field::val_xxx计算后进行输出。

调用顺序如下:

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

在这里插入图片描述
小TIPS:如果内核需要实现多线程并行计算聚合函数的时候,可以通过改造对中间结果输出save_in_field_inner函数,让每个中间结果如按照设计保存到相应的field->ptr中,保留到临时表中。

一些函数(待补全)

Item_sum::check_sum_func

验证聚合函数的语义要求。检查聚合函数的上下文是否允许对其进行聚合,并且当它是另一个聚合函数的参数时,需要直接或间接确保该函数将这两个聚合函数聚合在不同的查询块中。如果将聚合函数聚集在某个外部查询块中,则会将其添加到附加的聚集块查询item链inner_sum_func_list中。
tips:解析表达式时,必须为所有的聚合函数调用此函数,而且是以后缀顺序调用。

Item_sum::cleanup

使用后调用每个item。作用是释放所有分配的资源,例如动态内存。通过清除缓存的值为新的执行做准备。它不会删除准备期间分配的值,那些资源由析构函数释放。

参考

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 性能优化实战记录(2)---句柄泄漏和监控

前言: Java不存在内存泄漏, 但存在过期引用以及资源泄漏. (个人看法, 请大牛指正) 这边对文件句柄泄漏的场景进行下模拟, 并对此做下简单的分析.如下代码为模拟一个服务进程, 忽略了句柄关闭, 造成不能继续正常服务的小场景. 1 public class FileHandleLeakExample {2 3 p…

什么是Java文件?

Java文件 (Java files) The file is a class of java.io package. 该文件是java.io包的类。 If we create a file then we need to remember one thing before creating a file. First, we need to check whether a file exists of the same name or not. If a file of the sa…

绕过本地验证提交HTML数据

我们在入侵一个网站,比如上传或者自己定义提交的文件时,会在本地的代码中遇到阻碍,,也就是过 滤,过滤有两种,一种是在远程服务器的脚本上进行的过滤,这段代码是在服务器上运行后产生作用的,这种过 滤方式叫做远程过滤;另一种是在我们的IE浏览器里执行的脚本过滤,就是说是在我们…

《dp补卡——343. 整数拆分、96. 不同的二叉搜索树》

343. 整数拆分 1、确定dp数组以及下标含义。 dp[i]&#xff1a;分拆数字i&#xff0c;可以得到的最大的乘积 2、确定递推公式&#xff1a; dp[i]最大乘积出处&#xff1a;从1遍历j到i&#xff0c;j * dp[i-j] 与 j * (i-j)取最大值。( 拆分j的情况&#xff0c;在遍历j的过程…

Adroid学习之 从源码角度分析-禁止使用回退按钮方案

有时候&#xff0c;不能让用户进行回退操作&#xff0c;如何处理&#xff1f; 查看返回键触发了哪些方法。在打开程序后把这个方法禁止了。问题&#xff1a;程序在后台驻留&#xff0c;这样就会出现&#xff0c;其他时候也不能使用回退按钮。如何处理&#xff0c;在onpase()时方…

骑士游历问题问题_骑士步行问题

骑士游历问题问题Problem Statement: 问题陈述&#xff1a; There is a chessboard of size NM and starting position (sx, sy) and destination position (dx,dy). You have to find out how many minimum numbers of moves a knight goes to that destination position? 有…

Android基础之用Eclipse搭建Android开发环境和创建第一个Android项目(Windows平台)...

一、搭建Android开发环境 准备工作&#xff1a;下载Eclipse、JDK、Android SDK、ADT插件 下载地址&#xff1a;Eclipse:http://www.eclipse.org/downloads/ JDK&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/jdk7u9-downloads-1859576.html Android SD…

《dp补卡——01背包问题》

目录01背包[416. 分割等和子集](https://leetcode-cn.com/problems/partition-equal-subset-sum/)[1049. 最后一块石头的重量 II](https://leetcode-cn.com/problems/last-stone-weight-ii/)[494. 目标和](https://leetcode-cn.com/problems/target-sum/)01背包 1、dp数组以及…

用JavaScript往DIV动态添加内容

参考&#xff1a;http://zhidao.baidu.com/link?url6jSchyqPiEYCBoKdOmv52YHz9r7MTBms2pK1N6ptOX1kaR2eg320mlW1Sr6n36hpOeOadBxC2rWWGuhZPbms-K <div id"show"></div>要填充的数据为: 这是一个测试例子.jquery&#xff1a;$(function(){ var data …

《dp补卡——完全背包问题》

N件物品和一个最多能背重量为W的背包。第i件物品的重量为weight[i]&#xff0c;得到的价值是value[i]。每件物品都有无限个(可以放入背包多次)&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 01背包和完全背包唯一不同在于遍历顺序上。 01背包的核心代码&#xff1a…

Java中的类型转换

类型转换 (Typecasting) Typecasting is a term which is introduced in all the language similar to java. Typecasting是一个用与Java类似的所有语言引入的术语。 When we assign primitive datatype to another datatype. 当我们将原始数据类型分配给另一个数据类型时。 I…

让crash文件中的内存地址变成函数名称,

假如程序员编译了inhouse给测试。 如果在测试过程中出现奔溃现象&#xff0c;我想程序员一般会来看Device Log 也就是 crash文件 如果crash文件遇到如下的情况&#xff0c;在重要的地方看不到函数名称。我想是一件很奔溃的事情。 1 Exception Type: EXC_BAD_ACCESS (SIGSEGV)2…

《dp补卡——多重背包》

多重背包简介&#xff1a; 有N种物品和一个容量为V的背包。第i种物品最多有Mi件可用&#xff0c;每件耗费的空间为Ci&#xff0c;价值为Wi。求解将哪些物品装入背包可使得这些物品耗费的空间总和不超过背包容量&#xff0c;且价值总和最大。 将Mi件摊开&#xff0c;就是一个01背…

kafka消息确认ack_什么是确认(ACK)? ACK代表什么?

kafka消息确认ackACK&#xff1a;致谢 (ACK: Acknowledgment) An acknowledgment (ACK) is a signal that is passed among the communicating processes, computers, or devices to indicate acknowledgment, or delivery of the message, as a component of a communications…

CocoaAsyncSocket 套接字

CocoaAsyncSocket 套接字 https://github.com/robbiehanson/CocoaAsyncSocket Asynchronous socket networking library for Mac and iOS 用于iOS以及Mac的异步套接字网络库。 TCP GCDAsyncSocket and AsyncSocket are TCP/IP socket networking libraries. Here are the key…

谷歌浏览器设置缓存方法

谷歌浏览器设置缓存方法&#xff1a; 1、在桌面Google Chrome快捷方式&#xff0c;目标&#xff1a;找到 C:\Users\Splendid\AppData\Local\…\Application\chrome.exe 在这后面加上-Disk-Cache-Dir”Z:\TEMP” 注意: -Disk前面有空格&#xff0c;”Z:\TEMP” 是文件存放在Z盘T…

《dp补卡——买卖股票问题》

目录121. 买卖股票的最佳时机贪心dp思路滚动数组优化122. 买卖股票的最佳时机 II123. 买卖股票的最佳时机 III188. 买卖股票的最佳时机 IV309. 最佳买卖股票时机含冷冻期714. 买卖股票的最佳时机含手续费121. 买卖股票的最佳时机 贪心 取最左最小值&#xff0c;取最右最大值&…

oo0ooo0ooo0oo_OoO的完整形式是什么?

oo0ooo0ooo0ooOoO&#xff1a;外出 (OoO: Out of Office) OoO is an abbreviation of "Out of Office". OoO是“不在办公室”的缩写。 It is an expression, which is commonly used in the Gmail platform. It is written in the body or the subject of the email…

SP2010开发和VS2010专家食谱--第三章节--高级工作流(2)--为沙盒解决方案创建自定义活动...

尽管沙河解决方案功能有限&#xff0c;你仍然可以开发自定义活动&#xff0c;在SharePoint Designer中使用而不用改变web.config或添加.ACTION文件到根文件夹。 转载于:https://www.cnblogs.com/crazygolf/p/3856795.html

sql where 1=1和 0=1 的作用

where 11; 这个条件始终为True&#xff0c;在不定数量查询条件情况下&#xff0c;11可以很方便的规范语句。 一、不用where 11 在多条件查询中的困扰 举个例子&#xff0c;如果您做查询页面&#xff0c;并且&#xff0c;可查询的选项有多个&#xff0c;同时&#xff0c;还让用户…