news 2026/9/18 1:35:11

StarRocks 日期函数 date_sub / subdate 完全指南:语法、参数、返回值与底层实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
StarRocks 日期函数 date_sub / subdate 完全指南:语法、参数、返回值与底层实现解析

StarRocks 日期函数 date_sub / subdate 完全指南:语法、参数、返回值与底层实现解析

【免费下载链接】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

date_sub(别名subdate)是 StarRocks 中用于从日期时间值中减去指定时间间隔的核心函数,广泛应用于时间窗口过滤、滚动区间统计、增量数据回溯等场景。本文以官方文档 date_sub.md 为骨架,结合 StarRocks FE(前端)与 BE(后端)源码,系统讲解其语法、参数单位、边界行为、完整示例,以及从 SQL 解析、常量折叠到向量化执行的底层实现链路,帮助你准确、高效地使用这一日期时间函数族。

函数概述:date_sub 与 subdate 的关系

date_subsubdate完全等价的两个函数名,语义完全相同——都是从给定的日期或日期时间中减去指定长度的时间间隔。在 StarRocks 的函数注册表中,两者被同时注册:

  • FunctionSet.java 中分别定义了DATE_SUB = "date_sub"SUBDATE = "subdate"两个函数名常量;
  • ScalarOperatorFunctions.java 通过@ConstantFunction注解为subdatedate_sub注册了相同的函数签名(DATETIME, INT) -> DATETIME,且标记为isMonotonic = true(单调函数,详见下文"谓词化简"一节)。

因此,在实际 SQL 中你可以根据团队编码习惯任选其一,两者在功能、性能与返回值上没有任何差异。

语法与参数详解

函数签名

DATETIME DATE_SUB(DATETIME|DATE date, INTERVAL expr type)

参数说明

参数类型要求含义
dateDATE 或 DATETIME合法的日期/日期时间表达式,是减法运算的基准时间
exprINT要减去的时间间隔数值(可为负数,负数等价于加上该间隔)
type时间单位关键字时间间隔的单位,决定expr的语义

type支持的时间单位

单位关键字说明引入版本
YEAR全版本
QUARTER季度全版本
MONTH全版本
DAY全版本
HOUR小时全版本
MINUTE分钟全版本
SECOND全版本
MILLISECOND毫秒3.1.7 及以后
MICROSECOND微秒3.1.7 及以后

版本注意MILLISECONDMICROSECOND两个单位自 StarRocks 3.1.7 起才可用,低于该版本会报语法错误。这与 BE 侧time_functions.cpp中按单位宏展开的millis_add/millis_submicros_add/micros_sub函数族一一对应,见下文"BE 端实现"。

语法细节

  • 函数名大小写不敏感,date_subDATE_SUB均合法;
  • 单位关键字同样不区分大小写,INTERVAL 2 DAYINTERVAL 2 day等价(示例中两种写法均有出现);
  • date既可以是字符串字面量(如'2010-11-30 23:59:59'),也可以是DATE/DATETIME类型的列、表达式或函数返回值。

返回值与边界行为

date_sub始终返回DATETIME 类型的值,返回值保留原时间的小时、分钟、秒及小数部分。需要特别注意以下边界行为:

  1. 无效日期返回 NULL:如果输入的日期本身不存在,例如2020-02-30(2 月没有 30 号),函数直接返回NULL,而不是执行"进位修正"。
  2. 类型不合法返回 NULL:若date不是合法的 DATE 或 DATETIME 值,同样返回NULL
  3. DATE 类型输入自动按 DATETIME 处理:当输入为纯日期(如'2010-11-30')时,函数会将其视为2010-11-30 00:00:00参与运算,结果仍以 DATETIME 返回。这在下面的示例中可以看到:减去 2 小时后得到2010-11-29 22:00:00,体现了"借位到前一天"的日期回退逻辑。
  4. 跨月/跨年借位:按 DAY 等短单位做减法时,会自动进行借月、借年运算,正确处理闰年与大小月。
  5. 月份相关的月末溢出语义:按 MONTH/QUARTER/YEAR 做减法时,StarRocks 遵循"直接减月份字段,超出月末则返回该月最后一天"的常见日期库语义(即2010-03-31减 1 个月得到2010-02-28)。

完整示例

以下是官方文档 date_sub.md 给出的全部示例,逐一展开说明:

-- 示例 1:减去 2 天,时间部分保持不变 select date_sub('2010-11-30 23:59:59', INTERVAL 2 DAY); +-------------------------------------------------+ | date_sub('2010-11-30 23:59:59', INTERVAL 2 DAY) | +-------------------------------------------------+ | 2010-11-28 23:59:59 | +-------------------------------------------------+ -- 示例 2:DATE 类型输入按 00:00:00 处理,减去 2 小时发生跨天借位 select date_sub('2010-11-30', INTERVAL 2 hour); +-----------------------------------------+ | date_sub('2010-11-30', INTERVAL 2 HOUR) | +-----------------------------------------+ | 2010-11-29 22:00:00 | +-----------------------------------------+ -- 示例 3:无效日期(2020 年 2 月没有 30 号)返回 NULL select date_sub('2010-02-30', INTERVAL 2 DAY); +----------------------------------------+ | date_sub('2010-02-30', INTERVAL 2 DAY) | +----------------------------------------+ | NULL | +----------------------------------------+ -- 示例 4:减去 2 个季度(共 6 个月) select date_sub('2010-11-30 23:59:59', INTERVAL 2 QUARTER); +-----------------------------------------------------+ | date_sub('2010-11-30 23:59:59', INTERVAL 2 QUARTER) | +-----------------------------------------------------+ | 2010-05-30 23:59:59 | +-----------------------------------------------------+ -- 示例 5:使用别名 subdate 减去 2 毫秒(3.1.7 及以后) select subdate('2010-11-30 23:59:59', INTERVAL 2 millisecond); +--------------------------------------------------------+ | subdate('2010-11-30 23:59:59', INTERVAL 2 MILLISECOND) | +--------------------------------------------------------+ | 2010-11-30 23:59:58.998000 | +--------------------------------------------------------+ -- 示例 6:减去 2 微秒,返回微秒精度(3.1.7 及以后) select date_sub('2010-11-30 23:59:59', INTERVAL 2 microsecond); +---------------------------------------------------------+ | date_sub('2010-11-30 23:59:59', INTERVAL 2 MICROSECOND) | +---------------------------------------------------------+ | 2010-11-30 23:59:58.999998 | +---------------------------------------------------------+

补充示例:负的 expr

expr支持 INT 类型负数,INTERVAL -2 DAY等价于加 2 天。这一特性可用于在一条 SQL 中同时表达"回退"与"前推"语义:

select date_sub('2010-11-30 23:59:59', INTERVAL -2 DAY); -- 结果:2010-12-02 23:59:59

与其他日期时间函数的对照

为便于选型,这里给出与date_sub同族或易混淆函数的对照(相关文档见 日期时间函数索引):

函数语义典型场景
date_sub/subdate减去指定时间间隔,单位覆盖年/季/月/日/时/分/秒/毫秒/微秒通用时间回退计算
date_add/adddate加上指定时间间隔,与date_sub互为逆运算过期时间、未来时间计算,文档见 date_add.md
days_sub/days_add仅支持按天为单位的加减性能敏感且只需按天计算的场景,文档见 days_sub.md
timestampadd标准 SQL 风格:timestampadd(unit, interval, datetime)需要符合 ANSI SQL 语法的场景,文档见 timestampadd.md
years_sub/months_sub单单位专用函数,如 years_sub.md、months_sub.md只针对单一时间单位、且希望函数签名更明确的场景

从实现上看,这一整族"按单位加减"的函数在 BE 端由同一套宏模板统一生成,见下节源码分析。

源码实现原理

FE 端:解析、重写与常量折叠

date_sub家族(date_addadddatedate_subsubdatedays_sub)在 StarRocks FE 的 SQL 解析阶段即被特殊处理。AstBuilder.java 中定义了DATE_FUNCTIONS列表,注释明确指出:这类日期函数在解析时会被重写为TimestampArithmeticExpr(一种专门的"时间戳算术表达式"节点),而非普通函数调用。这意味着:

  • 语法上date_sub(date, INTERVAL expr type)date - INTERVAL expr type的算术写法被统一为同一类表达式;
  • 后续优化器可对这类表达式进行专门的下推、化简与常量折叠。

常量折叠:当dateexpr均为常量时,FE 会在优化阶段直接算出结果。这在 ScalarOperatorFunctions.java 中通过@ConstantFunction(name = "date_sub", argTypes = {DATETIME, INT}, returnType = DATETIME, isMonotonic = true)注册——isMonotonic = true表明该函数关于时间参数是单调的。

谓词化简(monotonic 的应用):正因为date_sub被标记为单调函数,SimplifiedPredicateRule.java 将其纳入TIME_FNSTIME_FN_NAMES映射表,用于谓词等价变换。例如源码注释中给出的化简规则(L442):

-- 优化器将嵌套的时间函数化简,减少一次计算 date_sub(date_add(x, 1), 2) -> date_sub(x, 1)

这意味着在写 SQL 时,即使表达式被写成了嵌套形式,优化器也会自动合并化简,不会产生多余的计算开销。

BE 端:向量化执行与模板化实现

BE(后端)侧的实现位于 time_functions.cpp(约 L944-L999),其核心是模板宏驱动的函数族生成

template <TimeUnit UNIT> TimestampValue timestamp_add(TimestampValue tsv, int count) { return tsv.add<UNIT>(count); } #define DEFINE_TIME_ADD_FN(FN, UNIT) ... { return timestamp_add<UNIT>(timestamp, value); } #define DEFINE_TIME_SUB_FN(FN, UNIT) ... { return timestamp_add<UNIT>(timestamp, -value); }

关键设计点:

  1. 减法是加法的负数形式DEFINE_TIME_SUB_FN直接复用timestamp_add<UNIT>,将value取负后调用,即"减 N 个单位 = 加 -N 个单位"。这与文档中expr支持负 INT 的行为完全一致。
  2. 一个模板覆盖全部单位:通过DEFINE_TIME_ADD_AND_SUB_FN(years, TimeUnit::YEAR)等宏调用,一次生成years_add/years_subquarters_add/quarters_submonths_add/months_subweeks_add/weeks_subdays_add/days_subhours_add/hours_subminutes_add/minutes_subseconds_add/seconds_submillis_add/millis_submicros_add/micros_sub共 20 个函数,覆盖了date_sub支持的全部type单位(含 3.1.7 起新增的毫秒、微秒)。
  3. 向量化执行:所有函数均返回ColumnPtr,通过DEFINE_TIME_CALC_FN定义(TYPE_DATETIME, TYPE_INT) -> TYPE_DATETIME的二元向量化签名,BE 会以批处理方式同时对整列数据求值,这正是 StarRocks 面向 OLAP 大规模列式计算性能的基础。
  4. 单元测试保障:time_functions_test.cpp 中包含了yearAddTestquarterAddTestmillisAddTestyearOverflowTest等测试用例,覆盖年/季度/毫秒加减及溢出场景,验证了各单位的计算正确性。

从 SQL 到结果的全链路

一条SELECT date_sub('2010-11-30 23:59:59', INTERVAL 2 DAY)的完整执行路径为:

  1. FE 解析AstBuilder识别date_sub属于DATE_FUNCTIONS家族,构建TimestampArithmeticExpr节点(见 AstBuilder.java);
  2. FE 优化:若参数全为常量,触发常量折叠直接产出结果;若出现在谓词中,利用单调性进行等价化简(见 SimplifiedPredicateRule.java);
  3. BE 执行:生成计划下发到 BE,调用time_functions.cpp中按TimeUnit模板实例化的days_sub等向量化函数,对整列批量求值(见 time_functions.cpp);
  4. 返回结果:以 DATETIME 列形式返回给客户端。

典型使用场景

1. 时间窗口过滤(滚动近 N 天数据)

-- 查询最近 7 天产生的订单 SELECT order_id, order_time, amount FROM orders WHERE order_time >= date_sub(now(), INTERVAL 7 DAY);

2. 环比/同比区间统计

-- 本周销售额与上周销售额对比 SELECT SUM(IF(ts >= date_sub(now(), INTERVAL 7 DAY), amount, 0)) AS this_week, SUM(IF(ts >= date_sub(now(), INTERVAL 14 DAY) AND ts < date_sub(now(), INTERVAL 7 DAY), amount, 0)) AS last_week FROM sales;

3. 与聚合函数配合生成时间分桶

-- 按"过去 30 天内每 7 天为一桶"聚合 SELECT date_sub(event_day, INTERVAL MOD(DATEDIFF(now(), event_day), 7) DAY) AS bucket_start, COUNT(*) FROM user_events WHERE event_day >= date_sub(now(), INTERVAL 30 DAY) GROUP BY bucket_start;

4. 物化视图 / 增量计算中的时间推进

date_sub也常与date_add成对出现在物化视图定义或 ETL 任务的时间游标计算中,例如MVMaintenance相关的时间推进逻辑中,通过date_sub(last_refresh_time, INTERVAL 1 DAY)回溯增量窗口起点。

注意事项与最佳实践

  1. 优先使用 DAY 等"字段无关"单位:对于只需要按天计算的场景,days_sub在语义上与date_sub(..., INTERVAL n DAY)等价,且函数签名更聚焦,可读性更好。
  2. 注意月末与季度边界:当date落在月末(如 3 月 31 日)时,按MONTH/QUARTER/YEAR减法会发生"月末收敛"(结果为 2 月 28/29 日)。若业务需要"同一天号",建议改用ADD_MONTHS语义或自行按DAY计算。
  3. 无效日期不会自动修正:传入2020-02-30之类不存在的日期会直接得到NULL,在 ETL 场景中建议结合COALESCECASE WHEN处理,避免 NULL 污染下游统计。
  4. 毫秒/微秒单位依赖版本:使用MILLISECONDMICROSECOND前请确认集群版本 ≥ 3.1.7,否则语句无法通过语法校验。
  5. 负间隔是合法输入expr支持 INT 负数,可等价实现"加法",但为可读性考虑,正向推进建议直接使用date_add(相关文档见 date_add.md)。
  6. 类型精度:返回值为 DATETIME,含微秒精度;若业务只关心日期部分,可再配合to_datedate_trunc收敛(参见 to_date.md)。

延伸阅读

  • 官方英文文档:date_sub.md、中文文档:date_sub.md
  • 逆运算:date_add.md、adddate.md
  • 单单位专用函数:days_sub.md、years_sub.md、months_sub.md、quarters_sub.md
  • SQL 标准风格:timestampadd.md、timestampdiff.md
  • 日期时间函数总览:date-time-functions.mdx
  • 源码参考:AstBuilder.java、ScalarOperatorFunctions.java、SimplifiedPredicateRule.java、time_functions.cpp、time_functions_test.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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 1:34:39

MATLAB直方图均衡化:histeq、手写算法与CLAHE对比

简介&#xff1a;这份文档是数字图像处理课程实验二的配套实验报告&#xff0c;面向高校电子信息、计算机及自动化等专业修读图像处理课程的学生&#xff0c;以及需要完成MATLAB图像增强实验的学习者。内容围绕直方图均衡化展开&#xff0c;涵盖实验目的、设备要求、图像增强原…

作者头像 李华
网站建设 2026/9/18 1:33:38

三相异步电机DTC仿真:状态闭环、离散决策与Simscape建模

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 1:33:05

随身鹿这类课堂录音工具,AI 总结和出题走 TaoToken 通道行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 1:31:47

单片机毕设项目:基于 STM32 或 51 单片机的物联网恒温饮水监测平台设计 基于 STM32 或 51 单片机的多维度状态监测智能水杯开发(025307)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/9/18 1:29:54

EgoScale:从第一人称视频到绳驱灵巧手的动作重定向实践

最近一直在折腾一个挺有意思的方向&#xff1a;如何让灵巧手像人手一样自然地去抓取、拿捏、转动东西。单独说起来&#xff0c;灵巧手这些年已经不算冷门&#xff0c;真正让我重新提起兴趣的&#xff0c;是“ego”这个前缀。把第一人称视角引入到整个控制链里之后&#xff0c;问…

作者头像 李华