news 2026/7/26 18:30:39

【Calcite 系列】深入理解 Calcite 的 AggregateExpandDistinctAggregatesRule

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Calcite 系列】深入理解 Calcite 的 AggregateExpandDistinctAggregatesRule

AggregateExpandDistinctAggregatesRule是 Calcite 中一条非常核心的聚合展开规则,它主要处理COUNT(DISTINCT x)SUM(DISTINCT x)这类带DISTINCT的聚合表达式。由于DISTINCT聚合往往不能直接下推到统一的物理实现中,Calcite 会在优化阶段把它改写成更基础的AggregateProjectJoinGROUPING SETS组合。本文结合源码实现,分析这条规则的匹配条件、几条主要改写路径、GROUPING SETS场景下的特殊处理,以及它与AGGREGATE_EXPAND_DISTINCT_AGGREGATES_TO_JOIN变体之间的关系。

1. 规则要解决什么问题

DISTINCT的聚合,本质上是在“先去重,再聚合”。例如:

SELECTdeptno,COUNT(DISTINCTename)FROMempGROUPBYdeptno;

从语义上看,它不是简单的:

GROUPBYdeptno

之后直接做一次COUNT就能完成的,因为COUNT看到的输入需要先在每个分组内按ename去重。

再比如:

SELECTdeptno,COUNT(DISTINCTename),SUM(sal)FROMempGROUPBYdeptno;

这里问题更复杂:

  • COUNT(DISTINCT ename)需要“分组内去重”;
  • SUM(sal)不需要去重;
  • 两种聚合的输入粒度并不一致。

这就是AggregateExpandDistinctAggregatesRule的存在价值。它要把这种“语义上复杂、执行上不统一”的聚合,改写成优化器和执行器更容易处理的 RelNode 组合。

对应源码位置:

  • core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java

规则类注释已经把核心思想说得很直接:

  • 如果所有DISTINCT聚合都作用在同一组参数上,一个额外的Aggregate往往就够了;
  • 如果有多组不同参数的DISTINCT聚合,就需要拆成多个Aggregate,再通过JoinGROUPING SETS组合回来。

2. 规则入口很简单,但前置判断很多

这条规则的匹配模式本身很简单,只匹配一个LogicalAggregate

b.operand(LogicalAggregate.class).anyInputs()

也就是说,它不是像很多规则那样要求固定的子树形状,而是只要看到Aggregate就先接管,再在onMatch里判断能不能改、该怎么改。

onMatch一进来先做两个硬判断:

  1. 当前Aggregate必须真的包含DISTINCT聚合;
  2. 如果当前配置不用GROUPING SETS,那遇到多groupSets的聚合要直接退出。

对应代码:

if(!aggregate.containsDistinctCall()){return;}if(!config.isUsingGroupingSets()&&aggregate.groupSets.size()>1){return;}

第二条很关键。因为这个规则有两个公开变体:

  • CoreRules.AGGREGATE_EXPAND_DISTINCT_AGGREGATES
  • CoreRules.AGGREGATE_EXPAND_DISTINCT_AGGREGATES_TO_JOIN

前者默认isUsingGroupingSets() == true,更偏向使用GROUPING SETS改写;

后者显式关闭GROUPING SETS,在复杂 distinct 场景下改用JOIN方式展开。

3. onMatch 的第一步:先把聚合调用分类

规则一开始会把所有聚合调用拆成几类:

  • distinctAggCalls:带DISTINCT的聚合;
  • nonDistinctAggCalls:不带DISTINCT的聚合;
  • filterCount:带FILTER的聚合个数;
  • distinctCallArgLists:按“参数列表 + filterArg”对 distinct 聚合分组。

这里最值得注意的是这句:

Pair.of(aggCall.getArgList(),aggCall.filterArg)

也就是说,Calcite 认为下面两种聚合不是同一种 distinct 模式:

C
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 14:35:24

如何有效屏蔽课堂上的经典小游戏:从扫雷到浏览器内置游戏

1. 为什么需要屏蔽课堂小游戏 作为一名有十年机房管理经验的技术老师,我见过太多学生偷偷打开扫雷、纸牌或者浏览器内置游戏的场景。这些看似无害的小游戏,实际上对课堂纪律和学习效率的影响远超想象。记得有一次期中考试复习课,我巡视机房时…

作者头像 李华