MySQL 源码|37 - 语法解析:聚集函数
源码位置(版本 = MySQL 8.0.37):
前置文档:
MySQL 语法解析的核心规则是 simple_expr
和 expr
,分别对应基础表达式和表达式。但是它们引用的其他规则很多,逻辑相对复杂,于是,我们先从一些相对独立的语法单元开始梳理,从而简化 simple_expr
和 expr
的引用关系。
不妨从聚集函数(aggregate function)的语法逻辑开始梳理,已知聚集函数的语法逻辑一定涉及 AVG
关键字,在 sql_yacc.yy
文件中搜索 AVG_SYM
,找到仅 sum_expr
规则包含此关键字。sum_expr
的关系图如下:
sum_expr
规则
在 sum_expr
中,涉及备选方案如下:
AVG
函数
标准语法:AVG([DISTINCT] expr) [over_clause]
用于计算 in_sum_expr
的平均值。有 AVG(in_sum_expr)
和 AVG(DISTINCT in_sum_expr)
两种备选语法,可以作为窗口函数。Bison 语法如下:
sum_expr:
AVG_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_avg(@$, $3, false, $5);
}
| AVG_SYM '(' DISTINCT in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_avg(@$, $4, true, $6);
}
BIT_AND
函数
标准语法:BIT_AND(expr) [over_clause]
用于计算 in_sum_expr
的按位与。只有 BIT_AND(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| BIT_AND_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_and(@$, $3, $5);
}
BIT_OR
函数
标准语法:BIT_OR(expr) [over_clause]
用于计算 in_sum_expr
的按位或。只有 BIT_OR(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| BIT_OR_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_or(@$, $3, $5);
}
JSON_ARRAYAGG
函数
标准语法:JSON_ARRAYAGG(col_or_expr) [over_clause]
用于将 in_sum_expr
中的值聚合为 Json 数组,详见 MySQL 8.0 官方手册:14.19.1 Aggregate Function Descriptions。只有 JSON_ARRAYAGG(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| JSON_ARRAYAGG '(' in_sum_expr ')' opt_windowing_clause
{
auto wrapper = make_unique_destroy_only<Json_wrapper>(YYMEM_ROOT);
if (wrapper == nullptr) YYABORT;
unique_ptr_destroy_only<Json_array> array{::new (YYMEM_ROOT)
Json_array};
if (array == nullptr) YYABORT;
$$ = NEW_PTN Item_sum_json_array(@$, $3, $5, std::move(wrapper),
std::move(array));
}
JSON_OBJECTAGG
函数
标准语法:JSON_OBJECTAGG(key, value) [over_clause]
用于以第 1 个 in_sum_expr
中的值为键,以第 2 个 in_sum_expr
中的值为值构造 Json 对象,详见 MySQL 8.0 官方手册:14.19.1 Aggregate Function Descriptions。只有 JSON_OBJECTAGG(in_sum_expr, in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| JSON_OBJECTAGG '(' in_sum_expr ',' in_sum_expr ')' opt_windowing_clause
{
auto wrapper = make_unique_destroy_only<Json_wrapper>(YYMEM_ROOT);
if (wrapper == nullptr) YYABORT;
unique_ptr_destroy_only<Json_object> object{::new (YYMEM_ROOT)
Json_object};
if (object == nullptr) YYABORT;
$$ = NEW_PTN Item_sum_json_object(
@$, $3, $5, $7, std::move(wrapper), std::move(object));
}
ST_COLLECT
函数
用于将 in_sum_expr
中的空间元素构造为数据集,详见 MySQL 8.0 官方手册:14.16.12 Spatial Aggregate Functions。有 ST_COLLECT(in_sum_expr)
和 ST_COLLECT(DISTINCT in_sum_expr)
两种备选语法, 可以作为窗口函数。Bison 语法如下:
| ST_COLLECT_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_collect(@$, $3, $5, false);
}
| ST_COLLECT_SYM '(' DISTINCT in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_collect(@$, $4, $6, true );
}
BIT_XOR
函数
标准语法:BIT_XOR(expr) [over_clause]
用于计算 in_sum_expr
的按位异或。只有 BIT_XOR(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| BIT_XOR_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_xor(@$, $3, $5);
}
COUNT
函数
标准语法:
COUNT(expr) [over_clause]
COUNT(DISTINCT expr,[expr...])
COUNT
函数有如下三种语法结构:
COUNT(ALL *)
或COUNT(*)
语法:计算所有记录数,可以作为窗口函数。Bison 语法如下:
| COUNT_SYM '(' opt_all '*' ')' opt_windowing_clause
{
$$= NEW_PTN PTI_count_sym(@$, $6);
}
COUNT(in_sum_expr)
语法:计算in_sum_expr
非空的记录数,可以作为窗口函数。Bison 语法如下:
| COUNT_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_count(@$, $3, $5);
}
COUNT(DISTINCT expr_list)
语法:计算expr_list
字段均不同的记录数,可以作为窗口函数。Bison 语法如下:
| COUNT_SYM '(' DISTINCT expr_list ')' opt_windowing_clause
{
$$= new Item_sum_count(@$, $4, $6);
}
MIN
函数
标准语法:MIN([DISTINCT] expr) [over_clause]
用于计算 in_sum_expr
的最小值。有 MIN(in_sum_expr)
和 MIN(DISTINCT in_sum_expr)
两种备选语法,可以作为窗口函数。根据 ANSI SQL 的标准,允许在 min
和 max
分组函数中使用 DISTINCT
关键字,但实际上不会改变函数的行为和结果,即 MIN|MAX(DISTINCT ...)
的处理方式与普通的 MIN|MAX()
没有区别。Bison 语法如下:
| MIN_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_min(@$, $3, $5);
}
| MIN_SYM '(' DISTINCT in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_min(@$, $4, $6);
}
MAX
函数
标准语法:MAX([DISTINCT] expr) [over_clause]
用于计算 in_sum_expr
的最大值。有 MAX(in_sum_expr)
和 MAX(DISTINCT in_sum_expr)
两种备选语法,可以作为窗口函数。根据 ANSI SQL 的标准,允许在 min
和 max
分组函数中使用 DISTINCT
关键字,但实际上不会改变函数的行为和结果,即 MIN|MAX(DISTINCT ...)
的处理方式与普通的 MIN|MAX()
没有区别。Bison 语法如下:
| MAX_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_max(@$, $3, $5);
}
| MAX_SYM '(' DISTINCT in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_max(@$, $4, $6);
}
STD
函数
标准语法:STD(expr) [over_clause]
用于计算 in_sum_expr
的总体标准差。只有 STD(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| STD_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_std(@$, $3, 0, $5);
}
VARIANCE
函数
标准语法:VARIANCE(expr) [over_clause]
用于计算 in_sum_expr
的总体平方差。只有 VARIANCE(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| VARIANCE_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_variance(@$, $3, 0, $5);
}
STDDEV_SAMP
函数
标准语法:STDDEV_SAMP(expr) [over_clause]
用于计算 in_sum_expr
的样本标准差。只有 STDDEV_SAMP(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| STDDEV_SAMP_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_std(@$, $3, 1, $5);
}
VAR_SAMP
函数
标准语法:VAR_SAMP(expr) [over_clause]
用于计算 in_sum_expr
的样本平方差。只有 VAR_SAMP(in_sum_expr)
一种备选语法,可以作为窗口函数。Bison 语法如下:
| VAR_SAMP_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_variance(@$, $3, 1, $5);
}
SUM
函数
标准语法:SUM([DISTINCT] expr) [over_clause]
用于计算 in_sum_expr
中元素之和。有 SUM(in_sum_expr)
和 SUM(DISTINCT in_sum_expr)
两种备选语法,可以作为窗口函数。Bison 语法如下:
| SUM_SYM '(' in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_sum(@$, $3, false, $5);
}
| SUM_SYM '(' DISTINCT in_sum_expr ')' opt_windowing_clause
{
$$= NEW_PTN Item_sum_sum(@$, $4, true, $6);
}
GROUP_CONCAT
函数
标准语法:
GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | expr}
[ASC | DESC] [,col_name ...]]
[SEPARATOR str_val])
用于将 expr_list
中的值拼接为字符串。在 expr_list
之前允许使用 opt_distinct
规则添加可选的 DISTINCT
关键字用于去重,在 expr_lsit
之后允许使用 opt_gorder_clause
关键字添加可选的 ORDER BY
子句用于排序,在可选的 ORDER BY
子句后允许使用 opt_gconcat_separator
规则添加可选的分隔符子句。Bison 语法如下:
| GROUP_CONCAT_SYM '(' opt_distinct
expr_list opt_gorder_clause
opt_gconcat_separator
')' opt_windowing_clause
{
$$= NEW_PTN Item_func_group_concat(@$, $3, $4, $5, $6, $8);
}
;
在 sum_expr
规则中,涉及了 in_sum_expr
、opt_windowing_clause
、opt_all
、expr_list
、opt_distinct
、opt_gorder_clause
和 opt_gconcat_separator
规则,其中:expr_list
是 expr
的列表,我们在梳理 expr
规则时梳理;opt_windowing_clause
是可选的窗口函数,我们在梳理窗口函数时梳理;opt_gorder_clause
是 ORDER BY
子句的延伸,我们在梳理 ORDER BY
规则时梳理。下面我们来看其他涉及的规则:
in_sum_expr
规则
在 sum_expr
规则中,几乎每个函数中的元素都是 in_sum_expr
规则。
in_sum_expr
规则对应标准语法 [ALL] expr
,包含一个可选的 ALL
关键字和一个普通表达式。Bison 语法如下:
in_sum_expr:
opt_all expr
{
$$= NEW_PTN PTI_in_sum_expr(@1, $2);
}
;
opt_all
规则
查看在 sum_expr
规则的 COUNT
函数备选方案以及在 in_sum_expr
规则中使用的 opt_all
规则。
opt_all
规则对应标准语法 [ALL]
,可以为空或为关键字 ALL
。Bison 语法如下:
opt_all:
%empty
| ALL
;
opt_distinct
规则
查看在 sum_expr
规则的 GROUP_CONCAT
函数备选方案中使用的 opt_distinct
规则。
opt_all
规则对应标准语法 [DISTINCT]
,可以为空或为关键字 DISTINCT
。Bison 语法如下:
opt_distinct:
%empty { $$ = 0; }
| DISTINCT { $$ = 1; }
;
opt_gconcat_separator
规则
查看在 sum_expr
规则的 GROUP_CONCAT
函数备选方案中使用的 opt_gconcat_separator
规则。
opt_gconcat_separator
规则对应标准语法 [SEPARATOR str_val]
,可以为空或为 SEPARATOR text_string
的语法形式。当为 SEPARATOR text_string
时,表示使用 text_string
作为分隔符。Bison 语法如下:
opt_gconcat_separator:
%empty
{
$$= NEW_PTN String(",", 1, &my_charset_latin1);
if ($$ == nullptr)
MYSQL_YYABORT;
}
| SEPARATOR_SYM text_string { $$ = $2; }
;