【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. 只在语义非常明确的场景下生效。

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

相关推荐
m0_702036539 分钟前
mysql如何处理不走索引的OR查询_使用UNION ALL优化重写
jvm·数据库·python
代钦塔拉34 分钟前
Qt4 vs Qt5 带参数信号槽的连接方式详解
开发语言·数据库·qt
2401_8463395634 分钟前
MySQL在云环境如何选择存储类型_SSD与高性能云盘配置建议
jvm·数据库·python
2601_957780841 小时前
GPT-5.5 深度解析:2026年4月OpenAI旗舰模型的技术跨越与商业决策指南
大数据·人工智能·python·gpt·openai
zhaoyong2222 小时前
SQL如何统计每个用户的首次行为时间_MIN聚合与分组
jvm·数据库·python
一只积极向上的小咸鱼2 小时前
嵌套 Git 仓库 / gitlink / submodule 问题总结
大数据·git·elasticsearch
2501_901006472 小时前
C#怎么实现配置热更新 C#如何在运行时动态刷新配置文件不需要重启程序【技巧】
jvm·数据库·python
m0_470857642 小时前
HTML怎么创建响应式图片备选方案_HTML srcset与sizes结构【详解】
jvm·数据库·python
lifewange2 小时前
PostgreSQL介绍
数据库·postgresql
oradh3 小时前
Oracle数据库中的Java概述
java·数据库·oracle·sql基础·oracle数据库java概述