ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

StarRocks SQL 函数 mod 详解:模运算的语法、数据类型与底层实现

StarRocks SQL 函数 mod 详解:模运算的语法、数据类型与底层实现 StarRocks SQL 函数 mod 详解模运算的语法、数据类型与底层实现【免费下载链接】starrocksThe worlds 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/starrocksmod(dividend, divisor)是 StarRocks 提供的求模取余函数返回被除数dividend除以除数divisor后的余数是日常 SQL 开发中最常用的数学函数之一。本文以 StarRocks 官方文档 为骨架结合 BEBackend端向量化实现与 FEFrontend端函数注册源码带你从语法、参数、返回值、边界行为到底层实现原理完整掌握mod的用法并能在数据分桶、循环分片、奇偶校验等场景中直接落地。函数概述与语法mod是 StarRocks 内置的二元数学函数语义与 C/C 的%运算符、标准库fmod一致计算dividend除以divisor后的余数。其 SQL 语法定义于官方文档mod(dividend, divisor)其中dividend被除数The number to be divideddivisor除数The number that divides。一个最直观的验证方式是在 mysql 客户端中直接执行mysql select mod(3.14,3.14); ----------------- | mod(3.14, 3.14) | ----------------- | 0 | -----------------当被除数能被除数整除时余数为 0上例中3.14 / 3.14 1余 0。支持的数据类型与隐式转换根据文档dividend和divisor均支持以下数据类型TINYINTSMALLINTINTBIGINTLARGEINTFLOATDOUBLEDECIMALV2DECIMAL32DECIMAL64DECIMAL128NOTEdividend和divisor必须在数据类型上保持一致。如果两者类型不一致StarRocks 会进行隐式类型转换。从 FE 端函数注册表 gensrc/script/functions.py 可以看到mod针对每种类型都有对应的注册条目与底层 C 实现绑定函数 ID函数名返回类型参数类型BE 端实现10250modTINYINTTINYINT, TINYINTMathFunctions::modTYPE_TINYINT10251modSMALLINTSMALLINT, SMALLINTMathFunctions::modTYPE_SMALLINT10252modINTINT, INTMathFunctions::modTYPE_INT10253modBIGINTBIGINT, BIGINTMathFunctions::modTYPE_BIGINT10254modLARGEINTLARGEINT, LARGEINTMathFunctions::modTYPE_LARGEINT10255modFLOATFLOAT, FLOATMathFunctions::fmodTYPE_FLOAT10256modDOUBLEDOUBLE, DOUBLEMathFunctions::fmodTYPE_DOUBLE10257modDECIMALV2DECIMALV2, DECIMALV2MathFunctions::modTYPE_DECIMALV2102570modDECIMAL32DECIMAL32, DECIMAL32MathFunctions::modTYPE_DECIMAL32102571modDECIMAL64DECIMAL64, DECIMAL64MathFunctions::modTYPE_DECIMAL64102572modDECIMAL128DECIMAL128, DECIMAL128MathFunctions::modTYPE_DECIMAL128102573modDECIMAL256DECIMAL256, DECIMAL256MathFunctions::modTYPE_DECIMAL256值得注意的两点实现事实注册表中除文档列出的类型外还额外包含DECIMAL256ID 102573说明当前仓库的mod支持范围已扩展至 256 位十进制类型对于FLOAT / DOUBLE浮点类型注册表绑定的并非mod模板而是MathFunctions::fmodTYPE_FLOAT/DOUBLE即底层复用 C 标准库的fmod函数来保证浮点取余的正确性见 be/src/exprs/math_functions.h。类型不一致时 StarRocks 会依据隐式转换规则统一类型后再计算例如INT与DOUBLE混用会提升为DOUBLE计算。返回值与边界行为文档对返回值给出了明确的约定Returns a value of the same data type as thedividend. StarRocks returns NULL ifdivisoris specified as 0.即返回类型与dividend被除数的数据类型一致。例如mod(11, -5)中两个参数都是整数返回整数 1除数为 0当divisor被指定为 0 时函数返回NULL而不是抛出除零错误或产生未定义结果。除零与溢出防护的底层处理在 BE 端实现 be/src/exprs/math_functions.h 中整数与 DecimalV2 的取模核心逻辑为// mod DEFINE_BINARY_FUNCTION_WITH_IMPL(modImpl, a, b) { // See pmodImpl: avoid SIGFPE on TYPE_MIN % -1. a % -1 0 for every a. if (b -1) { return ResultType(0); } return (a % (b (b 0))); } // mod for DecimalV2Value DEFINE_BINARY_FUNCTION_WITH_IMPL(modDecimalv2Impl, a, b) { return a % (b ((b DecimalV2Value::ZERO) ? DecimalV2Value::ONE : DecimalV2Value::ZERO)); }这里有三个关键设计SIGFPE 防护在 x86 平台上idiv指令计算TYPE_MIN % -1时会因为商溢出结果宽度而抛出硬件异常#DE表现为 SIGFPE。由于对任意a都有a % -1 0实现中在真正做除法前先对b -1短路返回 0从而避免进程被信号杀死。该防护逻辑在pmodImpl中同样存在见 be/src/exprs/math_functions.h。除零兜底b (b 0)的写法在除数为 0 时把除数临时替换为 1确保硬件除法不会触发除零异常真正的“返回 NULL”语义由外层RValueCheckZeroImpl检查右操作数是否为 0配合VectorizedUnstrictBinaryFunction的 NULL 处理机制完成见 be/src/exprs/math_functions.h 与 be/src/exprs/math_functions.h。DECIMAL 溢出双模式对于 DECIMAL32/64/128/256实现会根据context-error_if_overflow()选择溢出处理方式——OverflowMode::REPORT_ERROR报错或OverflowMode::OUTPUT_NULL输出 NULL中间结果超过目标 Decimal 精度与标度范围时按此策略收敛而标度遵循scale(a mod b) max(scale(a), scale(b))的规则见 be/src/exprs/arithmetic_operation.h 与 be/src/exprs/arithmetic_operation.h。负数取模的符号语义mod结果的符号跟随被除数与 C/C%语义一致这与返回非负余数的pmodpositive mod函数不同。文档示例充分体现了这一点select mod(11,-5); ------------ | mod(11, -5)| ------------ | 1 | ------------ select mod(-11,5); ------------- | mod(-11, 5) | ------------- | -1 | -------------mod(11, -5) 1除数为负但结果符号跟随被除数 11为正mod(-11, 5) -1被除数为负结果同样为负。如果需要“结果恒为非负”的取模语义例如轮询调度、循环分片场景应使用pmod(a, b)其实现会先fmod再加除数再取模以归一化到[0, b)区间见 be/src/exprs/math_functions.h。使用示例文档提供的完整示例可直接在 StarRocks 中执行验证mysql select mod(3.14,3.14); ----------------- | mod(3.14, 3.14) | ----------------- | 0 | ----------------- mysql select mod(3.14, 3); -------------- | mod(3.14, 3) | -------------- | 0.14 | -------------- select mod(11,-5); ------------ | mod(11, -5)| ------------ | 1 | ------------ select mod(-11,5); ------------- | mod(-11, 5) | ------------- | -1 | -------------补充几个典型的扩展场景-- 整数取模判断奇偶 SELECT mod(id, 2) AS parity FROM orders; -- 除数为 0 返回 NULL SELECT mod(10, 0); -- NULL -- 浮点取模底层走 fmod SELECT mod(5.5, 2.0); -- 1.5 -- 大整数取模 SELECT mod(9223372036854775807, 3); -- 与 BIGINT 溢出边界相关的取模结果 -- 与 % 运算符等价 SELECT 11 % 5, mod(11, 5); -- 1, 1在 StarRocks 中二元运算符%与mod函数共享同一套底层算子实现ModOp定义于 be/src/exprs/arithmetic_operation.h因此两者结果完全一致可互换使用。实际应用场景mod在数据分析与工程实践中非常高频常见用法包括奇偶 / 周期性判断mod(day_of_week, 2)划分工作日与休息日mod(month, 3)做季度分片数据分桶与抽样mod(hash(user_id), 100) 10取 10% 样本或按mod(id, N)均匀分发到 N 个任务桶轮询分配mod(row_number, worker_count)将记录循环分配给多个处理单元时间计算辅助结合时间戳计算偏移如mod(unix_timestamp(now()), 60)获取当前分钟内的秒偏移十进制业务计算金额拆分、重量分摊等场景下用mod校验分账合计与总数的尾差。常见问题与注意事项除数为 0 不会报错而是返回 NULL在 WHERE 条件中过滤mod(col, x) y时NULL 比较结果为非真注意用IS NULL单独处理。结果符号跟随被除数需要非负结果时改用pmod。返回类型跟随被除数mod(3.14, 3)返回浮点 0.14 而非整数 0类型由第一个参数决定。DECIMAL 溢出受精度限制DECIMAL32/64/128 参与取模时若结果超出精度与标度范围会根据溢出检查开关报错或返回 NULL对高精度需求可选用 DECIMAL128/DECIMAL256。TYPE_MIN % -1已做防护无需担心极端输入触发进程崩溃BE 端实现已短路处理。小结mod(dividend, divisor)虽语法简单但 StarRocks 在其背后覆盖了 11 种以上数据类型、隐式类型转换、除零返回 NULL、TYPE_MIN % -1的 SIGFPE 防护以及 Decimal 溢出双模式处理等完整工程化细节。从 官方文档 出发结合 函数注册表、BE 向量化实现 与 算术算子层即可全面掌握该函数的行为边界并在实际查询中放心使用。【免费下载链接】starrocksThe worlds 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),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进