MySQL 源码|38 - 语法解析:窗口函数

MySQL 源码|38 - 语法解析:窗口函数

源码位置(版本 = MySQL 8.0.37):

前置文档:

在 “37 - 语法解析:聚集函数” 中,我们梳理了聚集函数的相关逻辑,其中所有聚集函数都带有 opt_windowing_clause 规则,支持可选的窗口函数子句。

simple_expr 中,有两个备选方案与窗口函数有关,分别是 set_function_specification 规则和 window_func_call 规则,这两个规则的结果可以直接成为 simple_expr 规则的结果。

涉及窗口函数的规则如下图所示:其中绿色节点为本章节梳理,蓝色节点为之前章节已梳理,红色节点为后续章节梳理。

在这里插入图片描述

opt_windowing_clause 规则

查看在 sum_expr 中使用的 opt_windowing_clause 规则。其中有两种备选方案,分别为 %empty(空)和 windowing_clause 规则的结果。Bison 语法如下:

opt_windowing_clause:
          %empty
          {
            $$= nullptr;
          }
        | windowing_clause
          {
            $$= $1;
          }
        ;
set_function_specification 规则

查看 set_function_specification 规则的逻辑,其中有两种备选方案,分别是 sum_expr 规则和 grouping_operation 规则,sum_expr 规则即聚集函数,详见 MySQL 源码|37 - 语法解析:聚集函数,而 grouping_operation 规则为 GROUPING 子句。Bison 语法如下:

set_function_specification:
          sum_expr
        | grouping_operation
        ;
grouping_operation 规则

查看 set_function_specification 规则中用到的 grouping_operation 规则,其中只有 GROUPING(expr_list) 一个备选方案,用于返回 GROUPING() 函数超聚合结果。Bison 语法如下:

grouping_operation:
          GROUPING_SYM '(' expr_list ')'
          {
            $$= NEW_PTN Item_func_grouping(@$, $3);
          }
        ;
window_func_call 规则

window_func_call 规则中,包括所有不是聚合函数的窗口函数。对于窗口函数标准语法的描述,引用自 MySQL 官方手册:14.20.1 Window Function Descriptions

ROW_NUMBER() 函数

标准语法:ROW_NUMBER() over_clause

用于计算当前记录在当前窗口分区中的顺序号,此序号从 1 开始;当排序字段相同时,会导致非确定的顺序,但不会出现相同序号。只有 ROW_NUMBER() over_clause 这一种备选语法。Bison 语法如下:

          ROW_NUMBER_SYM '(' ')' windowing_clause
          {
            $$=  NEW_PTN Item_row_number(@$, $4);
          }
RANK() 函数

标准语法:RANK() over_clause

用于计算当前记录在当前窗口分区中的排名,此排名从 1 开始;当排序字段相同时,所有字段值相同的字段会使用相同的排名,后续记录会跳过这些记录中未出现的排名。例如两条记录并列第 1 名,那么下一条记录将是第 3 名。Bison 语法如下:

        | RANK_SYM '(' ')' windowing_clause
          {
            $$= NEW_PTN Item_rank(@$, false, $4);
          }
DENSE_RANK 函数

标准语法:DENSE_RANK() over_clause

用于计算当前记录在当前窗口分区中的排名,此排名从 1 开始;与 RANK() 函数不同的是,当出现相同排名时,后续记录不会跳过这些记录中未出现的排名。例如两条记录并列第 1 名,那么下一条记录将是第 2 名。Bison 语法如下:

        | DENSE_RANK_SYM '(' ')' windowing_clause
          {
            $$= NEW_PTN Item_rank(@$, true, $4);
          }
CUME_DIST() 函数

标准语法:CUME_DIST() over_clause

用于计算当前记录在当前窗口分区中的累积概率分布,即当前记录之前(含当前记录和与当前记录排序字段相同的记录)的记录数除以窗口分区中的总行数。Bison 语法如下:

        | CUME_DIST_SYM '(' ')' windowing_clause
          {
            $$=  NEW_PTN Item_cume_dist(@$, $4);
          }
PERCENT_RANK() 函数

标准语法:PERCENT_RANK() over_clause

用于计算当前记录在当前窗口分区中的排名百分比,即除最大值外,当前记录之前(不含当前记录)的记录数除以窗口分区中的总记录数,计算公式为 ( r a n k − 1 ) / ( r o w s − 1 ) (rank - 1) / (rows - 1) (rank1)/(rows1)。Bison 语法如下:

        | PERCENT_RANK_SYM '(' ')' windowing_clause
          {
            $$= NEW_PTN Item_percent_rank(@$, $4);
          }
NTILE() 函数

标准语法:NTILE(N) over_clause

将当前窗口分区分为 N 组(桶),并返回当前记录在当前窗口分区中分到的桶编号,返回值范围为 [ 1 , N ] [1, N] [1,N]。N 必须为正整数。Bison 语法如下:

        | NTILE_SYM '(' stable_integer ')' windowing_clause
          {
            $$=NEW_PTN Item_ntile(@$, $3, $5);
          }
LEAD() 函数

标准语法:LEAD(expr [, N[, default]]) [null_treatment] over_clause

获取在当前窗口分区中,当前记录之后的第 n 条记录;如果这条记录不存在,则返回 default。N 必须是非负整数。N 的默认值为 1,default 的默认值为 NULL。Bison 语法如下:

        | LEAD_SYM '(' expr opt_lead_lag_info ')' opt_null_treatment windowing_clause
          {
            PT_item_list *args= NEW_PTN PT_item_list(@expr, @opt_lead_lag_info);
            if (args == nullptr || args->push_back($3))
              MYSQL_YYABORT; // OOM
            if ($4.offset != nullptr && args->push_back($4.offset))
              MYSQL_YYABORT; // OOM
            if ($4.default_value != nullptr && args->push_back($4.default_value))
              MYSQL_YYABORT; // OOM
            $$= NEW_PTN Item_lead_lag(@$, true, args, $6, $7);
          }
LAG() 函数

标准语法:LAG(expr [, N[, default]]) [null_treatment] over_clause

获取在当前窗口分区中,当前记录之前的第 n 条记录;如果这条记录不存在,则返回 default。N 必须是非负整数。N 的默认值为 1,default 的默认值为 NULL。Bison 语法如下:

        | LAG_SYM '(' expr opt_lead_lag_info ')' opt_null_treatment windowing_clause
          {
            PT_item_list *args= NEW_PTN PT_item_list(@expr, @opt_lead_lag_info);
            if (args == nullptr || args->push_back($3))
              MYSQL_YYABORT; // OOM
            if ($4.offset != nullptr && args->push_back($4.offset))
              MYSQL_YYABORT; // OOM
            if ($4.default_value != nullptr && args->push_back($4.default_value))
              MYSQL_YYABORT; // OOM
            $$= NEW_PTN Item_lead_lag(@$, false, args, $6, $7);
          }
FIRST_VALUE() 函数

标准语法:FIRST_VALUE(expr) [null_treatment] over_clause

用于获取当前分区窗口中的第 1 条记录。Bison 语法如下:

        | FIRST_VALUE_SYM '(' expr ')' opt_null_treatment windowing_clause
          {
            $$= NEW_PTN Item_first_last_value(@$, true, $3, $5, $6);
          }
LAST_VALUE() 函数

标准语法:LAST_VALUE(expr) [null_treatment] over_clause

用于获取当前分区窗口中的最后 1 条记录。Bison 语法如下:

        | LAST_VALUE_SYM  '(' expr ')' opt_null_treatment windowing_clause
          {
            $$= NEW_PTN Item_first_last_value(@$, false, $3, $5, $6);
          }
NTH_VALUE() 函数

标准语法:NTH_VALUE(expr, N) [from_first_last] [null_treatment] over_clause

用于获取当前分区窗口中的第 N 条记录。Bison 语法如下:

        | NTH_VALUE_SYM '(' expr ',' simple_expr ')' opt_from_first_last opt_null_treatment windowing_clause
          {
            PT_item_list *args= NEW_PTN PT_item_list(@expr, @simple_expr);
            if (args == nullptr ||
                args->push_back($3) ||
                args->push_back($5))
              MYSQL_YYABORT;
            $$= NEW_PTN Item_nth_value(@$, args, $7 == NFL_FROM_LAST, $8, $9);
          }
        ;

window_func_call 规则中,引用了 windowing_clause 规则、stable_integer 规则、opt_lead_lag_info 规则、expr 规则、opt_lead_lag_info 规则、simple_expr 规则和 opt_from_first_last 规则。其中 expr 规则为一般表达式,simple_expr 规则为基础表达式,我们在梳理 expr 规则时一起处理。下面我们具体梳理其他涉及的规则。

stable_integer 规则

stable_integer 规则的结果实际上并不是常量,而不是在执行期间保持常量状态。stable_integer 规则有 int64_literal 规则和 param_or_var 规则两种备选方案。Bison 语法如下:

/*
  The stable_integer nonterminal symbol is not really constant, but constant
  for the duration of an execution.
*/
stable_integer:
          int64_literal  { $$ = $1; }
        | param_or_var
        ;
int64_literal 规则

int64_literal 规则有 NUMLONG_NUMULONGLONG_NUM 三种备选方案,分别对应 int 类型数值、long 类型数值和 u long long 类型数值。Bison 语法如下:

/*
  int64_literal if for unsigned exact integer literals in a range of
  [0 .. 2^64-1].
*/
int64_literal:
          NUM           { $$ = NEW_PTN Item_int(@$, $1); }
        | LONG_NUM      { $$ = NEW_PTN Item_int(@$, $1); }
        | ULONGLONG_NUM { $$ = NEW_PTN Item_uint(@$, $1.str, $1.length); }
        ;
param_or_var 规则

param_or_var 规则有 param_marker 规则、ident 规则以及 @ {规则:ident_or_text} 三种备选方案。其中 identident_or_text 规则属于语法元素的一部分,待梳理语法元素时梳理。Bison 语法如下:

param_or_var:
          param_marker { $$ = $1; }
        | ident        { $$ = NEW_PTN PTI_int_splocal(@$, to_lex_cstring($1)); }
        | '@' ident_or_text     { $$ = NEW_PTN PTI_user_variable(@$, $2); }
        ;
param_marker 规则

param_marker 规则只有 ? 开头的参数值一种备选方案(PARAM_MARKER)。Bison 语法如下:

param_marker:
          PARAM_MARKER
          {
            auto *i= NEW_PTN Item_param(@$, YYMEM_ROOT,
                                        (uint) (@1.raw.start - YYLIP->get_buf()));
            if (i == nullptr)
              MYSQL_YYABORT;
            auto *lex= Lex;
            /*
              If we are not re-parsing a CTE definition, this is a
              real parameter, so add it to param_list.
            */
            if (!lex->reparse_common_table_expr_at &&
                lex->param_list.push_back(i))
              MYSQL_YYABORT;
            $$= i;
          }
        ;
opt_lead_lag_info 规则

标准语法:[, N[, default]]

LEAD() 函数和 LAG() 函数中用到了 opt_lead_lag_info 规则。

opt_lead_lag_info 规则有空(%empty)和 , {stable_integer} {opt_ll_default} 两种备选方案。Bison 语法如下:

opt_lead_lag_info:
          %empty
          {
            $$.offset= nullptr;
            $$.default_value= nullptr;
          }
        | ',' stable_integer opt_ll_default
          {
            $$.offset= $2;
            $$.default_value= $3;
          }
        ;
opt_ll_default 规则

标准语法:[, default]

opt_ll_default 规则有空(%empty)和 , expr 两种备选方案。Bison 语法如下:

opt_ll_default:
          %empty
          {
            $$= nullptr;
          }
        | ',' expr
          {
            $$= $2;
          }
        ;
opt_from_first_last 规则

标准语法:[from_first_last]

NTH_VALUE() 窗口函数中用到了 from_first_last 规则。from_first_last 规则有如下 3 种备选方案:

  • 不匹配(%empty
  • FROM FIRSTFROM FIRST_SYM
  • FROM LASTFROM LAST_SYM

Bison 语法如下:

opt_from_first_last:
          %empty
          {
            $$= NFL_NONE;
          }
        | FROM FIRST_SYM
          {
            $$= NFL_FROM_FIRST;
          }
        | FROM LAST_SYM
          {
            $$= NFL_FROM_LAST;
          }
        ;
windowing_clause 规则

标准语法来源:MySQL 参考手册 - 14.20.2 Window Function Concepts and Syntax

标准语法(over_clause):{OVER (window_spec) | OVER window_name}

只有如下 1 种备选方案: OVER window_name_or_spec。Bison 语法如下:

windowing_clause:
          OVER_SYM window_name_or_spec
          {
            $$= $2;
          }
        ;
window_name_or_spec 规则

window_name_or_spec 规则包含如下 2 种备选方案:

  • window_name 规则
  • window_spec 规则

Bison 语法如下:

window_name_or_spec:
          window_name
          {
            $$= NEW_PTN PT_window(@$, $1);
          }
        | window_spec
          {
            $$= $1;
          }
        ;
window_name 规则

window_name 规则只有 ident 这 1 种备选方案,即只有一个语法元素作为名称。Bison 语法如下:

window_name:
          ident
          {
            $$= NEW_PTN Item_string($1.str, $1.length, YYTHD->charset());
          }
        ;
window_spec 规则

window_spec 规则只有 (window_spec_details) 这 1 种备选方案,即实现了窗口从句中的括号。Bison 语法如下:

window_spec:
          '(' window_spec_details ')'
          {
            $$= $2;
            if ($$ != nullptr) $$->m_pos = @$;
          }
        ;
window_spec_details 规则

标准语法来源:MySQL 参考手册 - 14.20.2 Window Function Concepts and Syntax

标准语法(window_spec):[window_name] [partition_clause] [order_clause] [frame_clause]

window_spec_details 规则只有一种备选方案,其中的 opt_existing_window_name 对应标准语法 [window_name]opt_partition_clause 对应标准语法 [partition_clause]opt_window_order_by_clause 对应标准语法 [order_clause]opt_window_frame_clause 对应标准语法 [frame_clause]。Bison 语法如下:

window_spec_details:
           opt_existing_window_name
           opt_partition_clause
           opt_window_order_by_clause
           opt_window_frame_clause
           {
             auto frame= $4;
             if (!frame) // build an equivalent frame spec
             {
               auto start_bound= NEW_PTN PT_border(POS(), WBT_UNBOUNDED_PRECEDING);
               auto end_bound= NEW_PTN PT_border(POS(), $3 ? WBT_CURRENT_ROW :
                 WBT_UNBOUNDED_FOLLOWING);
               auto bounds= NEW_PTN PT_borders(POS(), start_bound, end_bound);
               frame= NEW_PTN PT_frame(POS(), WFU_RANGE, bounds, nullptr);
               frame->m_originally_absent= true;
             }
             $$= NEW_PTN PT_window(POS(), $2, $3, frame, $1);
           }
         ;
opt_existing_window_name 规则

标准语法:[window_name]

opt_existing_window_name 规则有如下 2 种备选方案:

  • 不匹配
  • window_name:即使用一个语法元素作为已存在的窗口名称

Bison 语法如下:

opt_existing_window_name:
          %empty
          {
            $$= nullptr;
          }
        | window_name
          {
            $$= $1;
          }
        ;
opt_partition_clause 规则

标准语法:[partition_clause],其中 partition_clause: PARTITION BY expr [, expr] ...

opt_partition_clause 规则有如下 2 种备选方案:

  • 不匹配
  • PARTITION BY {group_list}:即使用 group_list 规则的匹配结果分区。group_list 待我们梳理 GROUP BY 子句时梳理。

Bison 语法如下:

opt_partition_clause:
          %empty
          {
            $$= nullptr;
          }
        | PARTITION_SYM BY group_list
          {
            $$= $3;
            if ($$ != nullptr) $$->m_pos = @$;
          }
        ;
opt_window_order_by_clause 规则

标准语法:[order_clause],其中 order_clause: ORDER BY expr [ASC|DESC] [, expr [ASC|DESC]] ...

opt_window_order_by_clause 规则有如下 2 种备选方案:

  • 不匹配
  • ORDER BY {order_list}:即使用 group_list 规则的匹配结果排序。order_list 待我们梳理 ORDER BY 子句时梳理。

Bison 语法如下:

opt_window_order_by_clause:
          %empty
          {
            $$= nullptr;
          }
        | ORDER_SYM BY order_list
          {
            $$= $3;
            if ($$ != nullptr) $$->m_pos = @$;
          }
        ;
opt_window_frame_clause 规则

标准语法来源:MySQL 参考手册 - 14.20.3 Window Function Frame Specification

标准语法:[frame_clause],其中 frame_clause: frame_units frame_extent

opt_window_frame_clause 规则有不匹配或连续匹配 window_frame_units 规则、window_frame_extent 规则和 opt_window_frame_exclusion 规则两种备选方案。Bison 语法如下:

opt_window_frame_clause:
          %empty
          {
            $$= nullptr;
          }
        | window_frame_units
          window_frame_extent
          opt_window_frame_exclusion
          {
            $$= NEW_PTN PT_frame(@$, $1, $2, $3);
          }
        ;
window_frame_units 规则

标准语法:frame_units: {ROWS | RANGE}

window_frame_units 规则有匹配 ROWS 关键字,匹配 RANGE 关键字和匹配 GROUPS 关键字三种备选方案。Bison 语法如下:

window_frame_units:
          ROWS_SYM    { $$= WFU_ROWS; }
        | RANGE_SYM   { $$= WFU_RANGE; }
        | GROUPS_SYM  { $$= WFU_GROUPS; }
        ;
window_frame_extent 规则

标准语法:frame_extent: {frame_start | frame_between}

window_frame_extent 规则有匹配 window_frame_start 规则结果和匹配 window_frame_between 规则结果两种备选方案。Bison 语法如下:

window_frame_extent:
          window_frame_start
          {
            auto end_bound= NEW_PTN PT_border(@$, WBT_CURRENT_ROW);
            $$= NEW_PTN PT_borders(@$, $1, end_bound);
          }
        | window_frame_between
          {
            $$= $1;
          }
        ;
window_frame_start 规则

标准语法:{CURRENT ROW | UNBOUNDED PRECEDING | UNBOUNDED FOLLOWING | expr PRECEDING | expr FOLLOWING}

window_frame_start 规则有如下 5 种备选方案:

  • 依次匹配 UNBOUNDED 关键字和 PRECEDING 关键字
  • 依次匹配 NUM_literal 规则匹配结果和 PRECEDING 关键字
  • 依次匹配 param_marker 规则匹配结果和 PRECEDING 关键字
  • 依次匹配 INTERVAL 关键字、expr 规则匹配结果、interval 规则匹配结果和 PRECEDING 关键字
  • 依次匹配 CURRENT 关键字和 ROW 关键字

其中 NUM_literal 规则和 interval 规则待数量数值类规则时梳理。

Bison 语法如下:

window_frame_start:
          UNBOUNDED_SYM PRECEDING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_UNBOUNDED_PRECEDING);
          }
        | NUM_literal PRECEDING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_VALUE_PRECEDING, $1);
          }
        | param_marker PRECEDING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_VALUE_PRECEDING, $1);
          }
        | INTERVAL_SYM expr interval PRECEDING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_VALUE_PRECEDING, $2, $3);
          }
        | CURRENT_SYM ROW_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_CURRENT_ROW);
          }
        ;
window_frame_between 规则

标准语法:BETWEEN frame_start AND frame_end

window_frame_between 规则只有依次匹配 BETWEEN 关键字、window_frame_bound 规则匹配结果、AND 关键字和 window_frame_bound 规则匹配结果这一种备选方案。Bison 语法如下:

window_frame_between:
          BETWEEN_SYM window_frame_bound AND_SYM window_frame_bound
          {
            $$= NEW_PTN PT_borders(@$, $2, $4);
          }
        ;
window_frame_bound 规则

标准语法:{CURRENT ROW | UNBOUNDED PRECEDING | UNBOUNDED FOLLOWING | expr PRECEDING | expr FOLLOWING}

window_frame_start 规则类似,window_frame_bound 规则有如下 5 种备选方案:

  • 匹配 window_frame_start 规则的匹配结果
  • 依次匹配 UNBOUNDED 关键字和 FOLLOWING 关键字
  • 依次匹配 NUM_literal 规则匹配结果和 FOLLOWING 关键字
  • 依次匹配 param_marker 规则匹配结果和 FOLLOWING 关键字
  • 依次匹配 INTERVAL 关键字、expr 规则匹配结果、interval 规则匹配结果和 FOLLOWING 关键字

Bison 语法如下:

window_frame_bound:
          window_frame_start
          {
            $$= $1;
          }
        | UNBOUNDED_SYM FOLLOWING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_UNBOUNDED_FOLLOWING);
          }
        | NUM_literal FOLLOWING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_VALUE_FOLLOWING, $1);
          }
        | param_marker FOLLOWING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_VALUE_FOLLOWING, $1);
          }
        | INTERVAL_SYM expr interval FOLLOWING_SYM
          {
            $$= NEW_PTN PT_border(@$, WBT_VALUE_FOLLOWING, $2, $3);
          }
        ;
opt_window_frame_exclusion 规则

opt_window_frame_exclusion 有如下 5 种备选方案:

  • 不匹配
  • 依次匹配 EXCLUDE 关键字、CURRENT 关键字和 ROW 关键字
  • 依次匹配 EXCLUDE 关键字和 GROUP 关键字
  • 依次匹配 EXCLUDE 关键字和 TIES 关键字
  • 依次匹配 EXCLUDE 关键字、NO 关键字和 OTHERS 关键字

Bison 语法如下:

opt_window_frame_exclusion:
          %empty
          {
            $$= nullptr;
          }
        | EXCLUDE_SYM CURRENT_SYM ROW_SYM
          {
            $$= NEW_PTN PT_exclusion(@$, WFX_CURRENT_ROW);
          }
        | EXCLUDE_SYM GROUP_SYM
          {
            $$= NEW_PTN PT_exclusion(@$, WFX_GROUP);
          }
        | EXCLUDE_SYM TIES_SYM
          {
            $$= NEW_PTN PT_exclusion(@$, WFX_TIES);
          }
        | EXCLUDE_SYM NO_SYM OTHERS_SYM
          { $$= NEW_PTN PT_exclusion(@$, WFX_NO_OTHERS);
          }
        ;
  • 14
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值