StarRocksyears_add日期时间函数完全指南:语法、示例与源码实现解析
【免费下载链接】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
导读
years_add是 StarRocks 内置的日期时间函数之一,用于在给定日期时间表达式上累加指定年数,广泛服务于数据仓库中的时间窗口计算、滚动年同比分析、计划表日期推算等场景。本文以官方文档为核心,结合 BE(Backend)与 FE(Frontend)源码,讲解其语法、返回类型、NULL 语义、常量折叠优化以及同系列日期加减函数的用法,帮助你准确、高效地使用这一函数。
函数签名与语义
语法
DATETIME YEARS_ADD(DATETIME expr1, INT expr2)expr1:一个合法的日期时间表达式,类型为DATETIME(也支持DATE类型输入,见下文源码说明)。expr2:要累加的年数,类型为INT,可以为正数、负数或 0。
函数返回在expr1基础上增加expr2年后的日期时间,时间单位为年。expr2为负数时等价于做减法——这也是 BE 源码中years_sub与years_add共用同一套实现的原因(见「源码实现解析」一节)。
返回值
- 输入为
DATETIME时,返回DATETIME; - 输入为
DATE时,返回DATE(FE 侧常量折叠路径中专门为{DATE, INT}组合注册了returnType = DATE的重载,见 ScalarOperatorFunctions.java)。
边界行为
- 若
expr1或expr2为 NULL,则结果为 NULL; - 若结果日期超出合法范围(如
0001-01-01减去多年、9999-12-31加上多年),函数返回 NULL。BE 侧在宏DEFINE_TIME_CALC_FN中通过date_valid<RESULT_TYPE>(p)对计算结果做合法性校验,不合法的时间值会被置空,参见 time_functions.cpp。
使用示例
以下示例来自官方文档,可直接在 StarRocks 中执行验证。
示例一:对完整 DATETIME 累加年份
select years_add('2010-11-30 23:50:50', 2); +-------------------------------------+ | years_add('2010-11-30 23:50:50', 2) | +-------------------------------------+ | 2012-11-30 23:50:50 | +-------------------------------------+可以看到,日期部分由2010变为2012,时分秒23:50:50保持不变。
示例二:对仅含日期(DATE)的输入累加年份
select years_add('2010-11-30', 2); +----------------------------+ | years_add('2010-11-30', 2) | +----------------------------+ | 2012-11-30 00:00:00 | +----------------------------+只提供日期时,函数按DATETIME语义处理,时间部分补零为00:00:00。
更多实用写法
- 负年数做回退计算:
SELECT years_add('2024-02-29 10:00:00', -4);返回2020-02-29 10:00:00。 - 与当前时间结合:
SELECT years_add(now(), 1);返回一年后的此刻。 - 结合聚合做同比:
SELECT years_add(event_date, -1) AS last_year_date, SUM(amount) FROM sales GROUP BY years_add(event_date, -1);可将本年度数据对齐到上一年同期进行比较。
源码实现解析
BE 端:向量化执行
years_add在 BE 端由宏展开生成,位于 time_functions.cpp:
template <TimeUnit UNIT> TimestampValue timestamp_add(TimestampValue tsv, int count) { return tsv.add<UNIT>(count); } #define DEFINE_TIME_ADD_FN(FN, UNIT) \ DEFINE_BINARY_FUNCTION_WITH_IMPL(FN##Impl, timestamp, value) { return timestamp_add<UNIT>(timestamp, value); } \ DEFINE_TIME_CALC_FN(FN, TYPE_DATETIME, TYPE_INT, TYPE_DATETIME); #define DEFINE_TIME_ADD_AND_SUB_FN(FN_PREFIX, UNIT) \ DEFINE_TIME_ADD_FN(FN_PREFIX##_add, UNIT); \ DEFINE_TIME_SUB_FN(FN_PREFIX##_sub, UNIT); // years_add // years_sub DEFINE_TIME_ADD_AND_SUB_FN(years, TimeUnit::YEAR);要点:
years_add被展开为TimeFunctions::years_add向量化函数,输入为TYPE_DATETIME、TYPE_INT,输出TYPE_DATETIME,通过VectorizedStrictBinaryFunction对整列数据进行批处理,满足 StarRocks 向量化执行引擎的要求;years_sub与years_add共用timestamp_add,只是将count取负,即timestamp_add<UNIT>(timestamp, -value);- 同一套宏还生成了
quarters_add/sub、months_add/sub、weeks_add/sub、days_add/sub、hours_add/sub、minutes_add/sub、seconds_add/sub、millis_add/sub、micros_add/sub等全套时间加减函数,时间单位由TimeUnit枚举控制。
TimestampValue::add的底层实现位于 timestamp_value.h:
TimestampValue add(int count) const { return TimestampValue{timestamp::add<UNIT>(_timestamp, count)}; }它直接调用timestamp::add<UNIT>完成年月日时分秒的进位/借位运算(如闰年 2 月 29 日加 4 年仍映射到 2 月 29 日、加 1 年则映射到 2 月 28 日),并在宏DEFINE_TIME_CALC_FN中通过date_valid检查结果的合法性,非法时间返回 NULL。
FE 端:常量折叠与单调性标记
在查询优化阶段,FE 会对常量输入做预计算(constant folding)。years_add的常量折叠实现位于 ScalarOperatorFunctions.java:
@ConstantFunction.List(list = { @ConstantFunction(name = "years_add", argTypes = {DATETIME, INT}, returnType = DATETIME, isMonotonic = true), @ConstantFunction(name = "years_add", argTypes = {DATE, INT}, returnType = DATE, isMonotonic = true) }) public static ConstantOperator yearsAdd(ConstantOperator date, ConstantOperator year) { if (date.getType().isDate()) { return ConstantOperator.createDateOrNull(date.getDatetime().plusYears(year.getInt())); } else { return ConstantOperator.createDatetimeOrNull(date.getDatetime().plusYears(year.getInt())); } }两点值得注意:
- 双重载:
{DATETIME, INT} → DATETIME与{DATE, INT} → DATE两个签名同时注册,保证 DATE 输入不会丢失日期语义; isMonotonic = true:标记该函数单调递增,优化器可据此对分区裁剪、排序等做更激进的优化,例如在范围查询中推导出更紧的谓词边界。
执行链路小结
从 SQL 到结果的整体链路为:SQL 解析 → FE 优化器(命中常量则走ScalarOperatorFunctions.yearsAdd直接算出结果)→ 生成执行计划下发 BE → BE 向量化执行TimeFunctions::years_add→timestamp::add<TimeUnit::YEAR>完成计算 →date_valid校验后返回。
常见问题与注意事项
- 类型匹配:
expr2必须是整数;传入小数需要先做类型转换或使用支持小数的函数,否则报错。 - 闰年边界:
years_add('2024-02-29', 1)的结果为2025-02-28,而不是2025-02-29(该日不存在),StarRocks 会做合理的日期归一化。 - NULL 传播:任一参数为 NULL 时结果为 NULL;非法日期结果同样返回 NULL,而非抛出异常。
- 与
date_add/date_sub的差异:years_add固定以「年」为单位,语义明确;如需灵活指定多种单位(年/月/日/时/分/秒),可考虑使用date_add(expr, INTERVAL n YEAR)形式的通用函数。
小结
years_add虽然语法简单,但其背后是 BE 向量化实现、时间单位模板化宏、FE 常量折叠与单调性优化等一整套工程机制。掌握它的签名、边界行为与源码路径,可以让你在实际查询中放心使用,并在需要时快速定位问题(如结果意外为 NULL 时优先检查日期合法性)。
如需了解完整函数族,可查阅 date-time-functions 目录下的各函数文档,或直接阅读 time_functions.cpp 中的宏定义部分。
【免费下载链接】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),仅供参考