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

AggregateValuesRule 处理的是 Aggregate 压在 Values 之上的极端小输入场景,尤其是空 Values 和"纯去重 aggregate"这两类情况。它不会试图通用执行所有聚合,而是专注做一些完全可以在优化阶段直接折叠的改写。本文结合源码实现,分析这条规则如何在空输入上直接构造聚合结果,以及它是怎样把 Aggregate(Values) 规约成去重后的 Values 的。

1. 规则要解决什么问题

有些查询在优化阶段就已经变成:

text 复制代码
Aggregate
  Values(...)

例如:

  • WHERE 1 = 0 之后的空输入;
  • IN (...) 常量列表展开后的 values;
  • 仅用 aggregate 去重的极小关系。

这时没必要等到执行期再跑真正的聚合算子。

2. 第一类场景:空 Values + 全局聚合

源码首先处理:

java 复制代码
aggregate.getGroupCount() == 0 && values.getTuples().isEmpty()

也就是 GROUP BY () 且输入为空。

这时它会直接按聚合函数种类构造结果字面量:

  • COUNT / SUM0 -> 0
  • MIN / MAX / SUM -> NULL

然后直接生成一个单行 Values 作为结果。

3. 为什么只支持这几类聚合

对于空输入,不同聚合函数的返回值语义并不统一。

源码只对以下几种做了明确支持:

  • COUNT
  • SUM0
  • MIN
  • MAX
  • SUM

其他未知聚合函数会直接退出,避免错误推断其空输入语义。

4. 第二类场景:Aggregate 只是做 DISTINCT

如果满足:

  1. Aggregate.isSimple(aggregate)
  2. aggCallList 为空;
  3. aggregate.getRowType().equals(values.getRowType())

说明这个 Aggregate 本质上只是对 Values 去重。

这时规则会直接:

java 复制代码
values.getTuples().stream().distinct()

把重复元组去掉,再生成一个新的 Values

5. 为什么要求 row type 相同

如果 aggregate 输出行类型和 values 输入行类型不一样,那么简单去重后直接返回 Values 可能会破坏 schema。

所以源码只在两者 row type 完全一致时,才把它规约成纯 Values

6. 这条规则为什么很有用

虽然它看起来只处理很小的 corner case,但这些场景在规则链里非常常见:

  • 常量折叠后产生空关系;
  • 子查询展开后变成 Values
  • IN 列表去重;
  • grand total on empty input。

如果不提前规约,后续规则和执行器都得带着这些"本可常量化"的节点继续走。

7. 代表性测试

RelOptRulesTest 中这条规则的直接测试非常集中:

  • testAggregateValuesRuleWithInRepeatingValues
  • testAggregateValuesRuleWithRepeatingValues
  • testAggregateValuesRuleWithAggregation
  • testAggregateValuesRuleWithSameRowType
  • testAggregateValuesRuleWithDifferentRowTypes
  • testEmptyAggregateEmptyKeyWithAggregateValuesRule

这些测试分别覆盖:

  • Values 去重;
  • 空输入上的聚合结果;
  • row type 相同/不同的区别;
  • grand total on empty input。

总结

AggregateValuesRule 是一条典型的"小输入折叠规则":

  1. Values + 全局聚合时,直接产出一行常量结果;
  2. 纯去重 Aggregate 时,直接把 Values 自身去重;
  3. 只在语义非常明确的场景下生效。

它的价值不在复杂度,而在于把一类完全可以在优化阶段完成的计算提前消掉。

相关推荐
考虑考虑5 小时前
数据库中的EXISTS
运维·数据库·后端
Wang's Blog5 小时前
Java框架快速入门: Spring Security+OAuth2之数据库和实体类的RBAC改造
java·数据库·spring
梦想平凡6 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
xcLeigh7 小时前
让大模型长出手脚,自己写SQL查数据库(Function Calling初探)
数据库·人工智能·sql·时序数据库·timechoai
yume_sibai8 小时前
06-Rust Web 开发实战(Axum 框架 + 数据库 + JWT 认证 + 中间件 + 部署)
前端·数据库·rust
云上先途8 小时前
属性标注是什么?主要解决什么问题?
大数据·人工智能
AugustRed9 小时前
Neo4j 图数据库原理 + 应用场景简单介绍
数据库·neo4j
Gl�ria9 小时前
Redis 高可用架构对比
数据库·redis
IpdataCloud9 小时前
高可用的IP数据接口平台有哪些?从可用性、P99延迟到离线部署的评估框架(含代码)
数据库·tcp/ip·ip
呆萌很10 小时前
MySQL 数据库和表的管理操作(命令行)
数据库·mysql