StarRocks SUM 聚合函数详解:语法、返回类型映射、NULL 处理与源码实现机制
2026/9/17 15:29:30 网站建设 项目流程

StarRocks SUM 聚合函数详解:语法、返回类型映射、NULL 处理与源码实现机制

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

本篇技术文章以 StarRocks 官方文档中的 SUM 函数说明 为主体,系统讲解SUM的语法、参数、返回值类型映射、NULL 忽略行为与隐式类型转换规则,并完整保留官方文档中的建表与查询示例;在此基础上,结合前端(FE)函数注册源码 FunctionSet.java 与后端(BE)聚合执行器 aggregator.h 的实现,深入剖析SUM在各数据类型下的返回类型推导与聚合计算流程。读完后,你既能正确编写含SUM的分析查询,也能理解 StarRocks 在解析器层面如何为SUM选择正确的返回类型,以及 VARCHAR 列隐式转 DOUBLE 的底层依据。

一、功能概述

SUM是 StarRocks 提供的聚合函数,用于返回表达式expr所有非 NULL 值的和。文档明确指出,可以配合DISTINCT关键字对去重后的非 NULL 值求和:

SUM([DISTINCT] expr)

这一语法形式在 聚合函数总览页 所属的文档体系中,SUMAVGCOUNTMINMAXSTDDEV等共同构成多维分析与实时分析场景中最基础的一组聚合原语。

二、参数说明

参数说明支持的数据类型
expr求值结果为数值的表达式TINYINT、SMALLINT、INT、FLOAT、DOUBLE、DECIMAL

官方文档给出的支持类型列表是“常用类型视角”。而从 FE 源码注册逻辑看,实际注册的类型覆盖面更广:registerBuiltinSumAggFunction方法会遍历FloatType.FLOAT_TYPESIntegerType.INTEGER_TYPESDecimalType.DECIMAL_TYPES三组类型族逐一注册聚合函数实例,因此 SMALLINT、BIGINT、LARGEINT、DECIMAL32/64/128/256 等变体在解析阶段都有对应的内置注册项(见 FunctionSet.java#L1714-L1742)。文档中列出的 TINYINT/SMALLINT/INT/FLOAT/DOUBLE/DECIMAL 是最常见的用户输入类型。

三、返回值:输入类型到返回类型的映射

这是SUM函数行为中最容易被开发者忽视、却直接影响结果列类型与精度的部分。官方文档给出的映射规则如下:

输入类型返回类型
TINYINTBIGINT
SMALLINTBIGINT
INTBIGINT
FLOATDOUBLE
DOUBLEDOUBLE
DECIMALDECIMAL

上述映射并非文档的孤立约定,而是 FE 在启动时注册聚合函数时逐一写死在注册逻辑中的。对应源码位于 FunctionSet.java#L1714-L1742:

  • 浮点族(FLOAT_TYPES,含 FLOAT 与 DOUBLE)SUM的中间结果类型与最终结果类型都注册为FloatType.DOUBLE,与文档中FLOAT -> DOUBLEDOUBLE -> DOUBLE一致;
  • 整型族(INTEGER_TYPES):若输入为LARGEINT,返回类型保持LARGEINT;其余整数类型(TINYINT/SMALLINT/INT/BIGINT)统一注册返回IntegerType.BIGINT,这正是文档中三行-> BIGINT映射的实现来源;
  • 十进制族(DECIMAL_TYPES):输入为 DECIMAL256 时返回 DECIMAL256,否则统一升宽为DecimalType.DECIMAL128,对应文档中DECIMAL -> DECIMAL的表述。源码中同时留有一个TODO(stephen): support auto scale up decimal precision注释,可以推断当前实现对 DECIMAL 的精度/scale 未做自动扩展,跨精度场景下建议关注该限制。

值得注意的一个设计细节:整型输入一律升宽为 BIGINT 返回,意味着即使对TINYINT列求和,结果列也是 64 位整数,为累加过程中的数值增长预留了空间;而LARGEINT(64 位整型)则原样保留。这种“输入窄化升宽、输入最宽保持”的策略与返回类型注册一一对应。

四、使用注意事项(Usage Notes)

官方文档列出了三条使用注意事项,每一条都有明确的实现依据,值得逐条展开:

4.1 忽略 NULL 值

This function ignores nulls.

SUM在累加过程中直接跳过 NULL 输入,而不是把 NULL 当作 0 或产生 NULL 结果(除非组内全部为 NULL)。后文的示例 2 将专门演示这一行为。

4.2 表达式不存在时报错

An error is returned ifexprdoes not exist.

SUM的参数不是合法表达式(例如引用了不存在的列)时,分析阶段即返回错误,不会进入执行阶段。

4.3 VARCHAR 输入的隐式转换

If a VARCHAR expression is passed, this function implicitly casts the input into DOUBLE values. If the cast fails, an error is returned.

这是SUM最“宽容”也最危险的一条行为:对字符串列求和时,StarRocks 会隐式地把每个值转换为 DOUBLE再累加。转换成功则按 DOUBLE 语义求和;某个值无法转换(例如字符串abc)时返回错误。官方文档的示例 3 正是基于这一规则,对STRING类型的hobby列执行SUM(DISTINCT hobby)

五、完整实战示例(继承官方文档操作链路)

以下示例完整继承官方文档中的四步操作链路:建表 → 插入数据 → 验证数据 → 四种SUM用法。

5.1 创建示例表 employees

CREATE TABLE IF NOT EXISTS employees ( region_num TINYINT COMMENT "range [-128, 127]", id BIGINT COMMENT "range [-2^63 + 1 ~ 2^63 - 1]", hobby STRING NOT NULL COMMENT "upper limit value 65533 bytes", income DOUBLE COMMENT "8 bytes", sales DECIMAL(12,4) COMMENT "" ) DISTRIBUTED BY HASH(region_num);

该表刻意混合了TINYINTBIGINTSTRINGDOUBLEDECIMAL五类列,正好覆盖SUM的主要类型路径,其中income列将插入一个 NULL 值用于演示 NULL 忽略行为。

5.2 插入数据

INSERT INTO employees VALUES (3,432175,'3',25600,1250.23), (4,567832,'3',37932,2564.33), (3,777326,'2',null,1932.99), (5,342611,'6',43727,45235.1), (2,403882,'4',36789,52872.4);

注意第三行id = 777326income为 NULL,这是后续示例 2 的伏笔。

5.3 验证表数据

MySQL > select * from employees; +------------+--------+-------+--------+------------+ | region_num | id | hobby | income | sales | +------------+--------+-------+--------+------------+ | 5 | 342611 | 6 | 43727 | 45235.1000 | | 2 | 403882 | 4 | 36789 | 52872.4000 | | 4 | 567832 | 3 | 37932 | 2564.3300 | | 3 | 432175 | 3 | 25600 | 1250.2300 | | 3 | 777326 | 2 | NULL | 1932.9900 | +------------+--------+-------+--------+------------+ 5 rows in set (0.01 sec)

5.4 示例 1:按区域计算销售总额

MySQL > SELECT region_num, sum(sales) from employees group by region_num; +------------+------------+ | region_num | sum(sales) | +------------+------------+ | 2 | 52872.4000 | | 5 | 45235.1000 | | 4 | 2564.3300 | | 3 | 3183.2200 | +------------+------------+ 4 rows in set (0.01 sec)

salesDECIMAL(12,4)列,返回值保持 DECIMAL 语义(3183.2200 = 1250.2300 + 1932.9900),与第三节“DECIMAL -> DECIMAL”的映射一致。

5.5 示例 2:NULL 值被忽略

MySQL > select region_num, sum(income) from employees group by region_num; +------------+-------------+ | region_num | sum(income) | +------------+-------------+ | 2 | 36789 | | 5 | 43727 | | 4 | 37932 | | 3 | 25600 | +------------+-------------+ 4 rows in set (0.01 sec)

region_num = 3的组内有两条记录,但id = 777326income为 NULL 未被计入,因此结果为单值25600。这正是“SUM ignores nulls”规则的直观验证:如果实现是“遇到 NULL 整组返回 NULL”,该组的输出将是 NULL 而非 25600。

5.6 示例 3:STRING 列隐式转 DOUBLE 求和 + DISTINCT

MySQL > select sum(DISTINCT hobby) from employees; +---------------------+ | sum(DISTINCT hobby) | +---------------------+ | 15 | +---------------------+ 1 row in set (0.01 sec)

hobby列是STRING类型,其值为'3','3','2','6','4'。按第 4.3 节的规则,每行值先被隐式转换为 DOUBLE;再按DISTINCT去重得到{3, 2, 6, 4},求和得15。这个例子同时验证了两个行为:字符串隐式数值转换,以及DISTINCT修饰符对非 NULL 去重值的求和。

5.7 示例 4:配合 WHERE 过滤后求和

MySQL > select sum(income) from employees WHERE income > 30000; +-------------+ | sum(income) | +-------------+ | 118448 | +-------------+ 1 row in set (0.00 sec)

过滤条件income > 30000保留了 37932、43727、36789 三行(25600 被过滤),合计118448SUMWHERE组合时,求和作用于过滤后的行集合,这是聚合函数与谓词下推协同的常规用法。

六、源码级机制补充:SUM 在 StarRocks 中的注册与执行

6.1 FE 侧:聚合函数的类型化注册

SUM作为内置聚合函数,在 FE 启动阶段由 FunctionSet.java 完成注册,入口是registerBuiltinSumAggFunction(SUM)(FunctionSet.java#L1514)。注册过程通过AggregateFunction.createBuiltin(name, 输入类型, 中间结果类型, 最终结果类型, ...)每一个具体的输入类型创建一条聚合函数实例,参数中的中间结果类型与最终结果类型共同决定了分布式聚合各阶段的输出类型:

  • 从源码结构看,整型路径中除 LARGEINT 外的所有整数类型统一以IntegerType.BIGINT作为中间与最终类型注册;
  • 浮点路径中 FLOAT 与 DOUBLE 均以FloatType.DOUBLE注册中间与最终类型;
  • DECIMAL 路径按 DECIMAL128/DECIMAL256 宽化处理。

这种“按类型逐一注册”的机制保证了类型推导发生在 FE 分析/规划阶段:用户写SUM(sales)时,返回列的类型在物理计划生成前就已经确定,BE 无需再做运行时类型协商。这也是文档中返回类型映射表能够精确给出的根本原因。

6.2 BE 侧:向量化聚合执行

在 BE 端,聚合算子由执行器中的聚合器框架承载,be/src/exec/aggregator.h 定义了聚合执行的核心流程,其注释明确描述了“消费完全部输入行后再进行 aggregate/finalize 处理”的分阶段模型。SUM的执行遵循该通用框架:各执行线程对分片数据累加得到部分结果,最终阶段完成合并输出。与 FE 的类型化注册配合,BE 侧按注册好的中间/最终类型完成数值累加,整型以 64 位宽度累加以避免溢出。

如果你需要验证SUM在不同类型下的行为,可以参考 BE 聚合相关测试目录 be/test/exec/ 与通用测试框架 be/test/test_main.cpp。

七、相关函数

SUM位于 StarRocks 聚合函数体系之中,文档目录 docs/en/sql-reference/sql-functions/aggregate-functions/ 下提供了与之配套的常用聚合函数文档,可按需延伸阅读:

  • multi_distinct_sum:对多列分别去重后求和;
  • avg:平均值聚合;
  • count:计数聚合;
  • min_max / max:最小值/最大值。

八、小结

SUM([DISTINCT] expr)是 StarRocks 多维分析场景中最常用的聚合原语之一。掌握它的关键有三点:一是返回类型映射——整数升宽为 BIGINT、浮点归一到 DOUBLE、DECIMAL 保持 DECIMAL(DECIMAL128/256);二是NULL 语义——累加过程直接跳过 NULL;三是字符串隐式转换——VARCHAR/STRING 输入按 DOUBLE 语义转换求和,转换失败则报错。FE 侧的类型化注册(FunctionSet.java)决定了前两点在规划期即被固化,BE 侧的向量化聚合框架(aggregator.h)则在执行期高效完成累加与合并。

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询