news 2026/9/18 3:06:03

StarRocks WEEK 函数完全指南:周数计算的 8 种模式与底层实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
StarRocks WEEK 函数完全指南:周数计算的 8 种模式与底层实现解析

StarRocks WEEK 函数完全指南:周数计算的 8 种模式与底层实现解析

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

WEEK()是 StarRocks 日期时间函数家族中用于计算给定日期所在周序号的函数,行为与 MySQL 的WEEK函数保持一致。在电商大促分析、财务周报、库存周维度聚合等“按周切片”的场景中,它是将日期列快速转换为周编号(INT)的核心工具。读完本文,你将掌握WEEK()的完整语法、8 种mode模式的取舍逻辑、边界日期(跨年周)的判定规则,以及该函数在 StarRocks BE 端 C++ 实现中的底层计算原理,从而在 SQL 中正确使用周维度统计,避免"周一对不上"的经典坑。

函数定位与版本支持

WEEK()返回指定日期在一年中的周序号(week number),自 StarRocksv2.3起支持。它接受DATETIMEDATE类型的日期参数,并可通过可选的mode参数控制“一周从周日还是周一开始”以及“周序号范围是 0–53 还是 1–53”,行为完全对齐 MySQL 的WEEK(date[, mode])语义。

在实际业务中,它与同族的日期函数形成互补关系:

  • WEEK():返回周序号(本文主题);
  • WEEKOFYEAR():等价于WEEK(date, 3),即默认从周一开始、范围 1–53 的 ISO 风格周号;
  • YEARWEEK():返回形如YYYYWW的"年+周"组合值;
  • DATE_TRUNC(date, week)/DATE_TRUNC(datetime, week):将日期截断到所在周的起始时刻,常用于周粒度聚合的分组键。

语法与参数说明

INT WEEK(DATETIME|DATE date, INT mode)

参数明细:

参数类型必选说明
dateDATETIME / DATE待计算周序号的日期;传入时间戳时仅取日期部分参与周计算
modeINT周计算逻辑开关,取值 0–7,默认0,缺省时按 mode 0 处理

mode参数同时控制两个维度:一周的第一天(Sunday 或 Monday),以及周序号范围(0–53 或 1–53),并进一步决定“第 1 周”的判定条件(是该年内第一个含周日的周,还是该年内第一个含 4 天及以上的周,或是第一个含周一的周)。完整规则如下表:

Mode一周的第一天周序号范围第 1 周是第一个满足以下条件的周
0Sunday0-53该年内包含一个周日
1Monday0-53该年内包含 4 天及以上
2Sunday1-53该年内包含一个周日
3Monday1-53该年内包含 4 天及以上
4Sunday0-53该年内包含 4 天及以上
5Monday0-53该年内包含一个周一
6Sunday1-53该年内包含 4 天及以上
7Monday1-53该年内包含一个周一

从这张表可以提炼出 8 种模式的三种"基因"组合:周起始日(Sunday/Monday)、返回值范围(0–53/1–53)、第 1 周判定(含周日 / 含周一 / 含 4 天以上)。其中:

  • ISO 8601 标准周对应mode = 3:周一为一周起点,范围 1–53,第 1 周必须包含该年的 4 天以上(即包含该年的第一个周四);
  • mode = 1mode = 3的区别仅在于是否允许返回 0(mode 1 返回 0–53,mode 3 返回 1–53),这正是WEEKOFYEAR()WEEK(date, 1)的差异所在。

返回值与边界行为

  • 返回类型为INT,取值范围 0–53,具体区间由mode决定;
  • date取值非法(如无法解析的日期)或输入为空(NULL)时,函数返回NULL
  • 跨年日期会因模式不同出现“回退到上一年最后一周”的情况,此时返回值可能是 52/53 或 0,详见下文示例。

实战示例:8 种模式对同一日期的结果差异

以下示例统一使用日历上的周一2007-01-01(2007 年 1 月 1 日),完整演示不同模式下同一日期的不同周号:

mode = 0,返回 0

一周从周日开始,2007-01-01是周一,不属于该年第一个"含周日的周"的第 1 周,因此返回0

mysql> SELECT WEEK('2007-01-01', 0); +-----------------------+ | week('2007-01-01', 0) | +-----------------------+ | 0 | +-----------------------+ 1 row in set (0.02 sec)

mode = 1,返回 1

一周从周一开始,2007-01-01恰为周一,直接命中第 1 周:

mysql> SELECT WEEK('2007-01-01', 1); +-----------------------+ | week('2007-01-01', 1) | +-----------------------+ | 1 | +-----------------------+ 1 row in set (0.02 sec)

mode = 2,返回 53

一周从周日开始,但2007-01-01是周一、不是周日;由于 mode 2 的范围是 1–53 且不允许返回 0,该日期被归属到上一年(2006 年)的最后一周,即第 53 周:

mysql> SELECT WEEK('2007-01-01', 2); +-----------------------+ | week('2007-01-01', 2) | +-----------------------+ | 53 | +-----------------------+ 1 row in set (0.01 sec)

这三个示例清晰展示了“0 与 53 其实是同一周在 0–53 / 1–53 两种范围下的不同呈现”:2007-01-01属于 2006 年的最后一周,在 0–53 模式下记作0(表示"尚未进入 2007 年第 1 周"),在 1–53 模式下记作53(上一年的最后一周)。

更多典型日期验证

结合源码测试用例(time_functions_test.cpp)中的数据,可以进一步验证各模式的行为:

日期mode=0mode=1mode=2mode=3mode=4mode=5mode=6mode=7
2007-01-01(周一)0153101521
2017-05-011818181818181818
2020-09-233839383938383939
2015-10-11(周日)4141414141414141

上表中 2007-01-01 各 mode 的结果来自本文示例与测试数据推断;2017-05-01、2020-09-23、2015-10-11 的 mode 3/2/1/0/5/7/4/6 结果直接取自 time_functions_test.cpp 中的weeks[]数组({1, 18, 39, 41, 49, 18, 5, 36} 分别对应 mode 3,2,1,0,5,7,4,6)。可见:

  • 2015-10-11 是周日,8 种模式结果全部一致(41),说明不落在跨年边界附近时,大多数模式结果趋同
  • 2020-09-23 在 mode 0/2/4/5 下为 38、在 mode 1/3/6/7 下为 39,体现了周一/周日起点对周归属的实质性影响。

在 SQL 中的典型用法

-- 统计每日销售额,并输出所在周序号(默认 mode 0) SELECT WEEK(sale_date) AS week_no, SUM(amount) AS total_amount FROM sales GROUP BY WEEK(sale_date) ORDER BY week_no; -- 使用 ISO 标准周(周一为一周起点) SELECT WEEK(sale_date, 3) AS iso_week_no, SUM(amount) AS total_amount FROM sales GROUP BY WEEK(sale_date, 3);

底层实现:从 SQL 到 C++ 内核

WEEK()并非单独的一个 C++ 函数,它在 StarRocks BE 端由week_of_year_with_default_mode(缺省 mode)与week_of_year_with_mode(显式 mode)两个向量化函数承载,二者定义于 time_functions.cpp,并最终统一调用核心计算函数TimeFunctions::compute_week()与模式归一化函数TimeFunctions::week_mode()

1. 模式归一化:week_mode()

源码 time_functions.cpp 将用户传入的 mode 折叠为三个位标志的组合:

uint TimeFunctions::week_mode(uint mode) { uint week_format = (mode & 7); if (!(week_format & WEEK_MONDAY_FIRST)) week_format ^= WEEK_FIRST_WEEKDAY; return week_format; }

三个位标志定义于 time_functions.h:

位标志含义
WEEK_MONDAY_FIRST1周一为一周第一天(否则为周日)
WEEK_YEAR2使用“周年”(week year)语义,涉及跨年归属
WEEK_FIRST_WEEKDAY4第 1 周判定依赖一周第一天(周日/周一)是否出现在该年内

其中if (!(week_format & WEEK_MONDAY_FIRST)) week_format ^= WEEK_FIRST_WEEKDAY;是一条关键变换:当一周从周日开始时,自动翻转WEEK_FIRST_WEEKDAY位,从而把"含周日"与"含周一"两种第 1 周判定统一到同一套算法中。

2. 周计算核心:compute_week()

TimeFunctions::compute_week(year, month, day, week_behaviour, *to_year)(time_functions.cpp)实现了与 MySQL 一致的周号算法,核心逻辑包括:

  1. 日序号(day number)换算:将年/月/日换算为自某基准日以来的累计天数daynr,并据此计算该日是周几;
  2. 年初边界判断:若日期落在 1 月且day <= 7 - weekday,说明该日期位于"跨年周"内,需要根据WEEK_YEARWEEK_FIRST_WEEKDAY决定其归属年份——要么归入上一年最后一周(周号可能为 52/53 或 0),要么仍算作新年第 1 周;
  3. 周号累加:以第 1 周为基准,用(daynr - 第一周起始日) / 7的偏移量累加得到周号;
  4. 年末边界修正:若week_year && days >= 52 * 7,说明日期可能落入 53 周区间,需再次校验是否真正属于第 53 周;
  5. 输出年份回写:通过*to_year输出实际归属的年份(可能为前一年),供YEARWEEK()等组合函数复用。

配套的compute_weekday(long daynr, bool sunday_first_day_of_week)(time_functions.cpp)负责基于daynr计算星期几,sunday_first_day_of_week参数即为"周日起始"与"周一起始"的开关。

3. 向量化执行与测试验证

BE 端通过DEFINE_TIME_UNARY_FN/DEFINE_TIME_BINARY_FN宏将上述实现包装为可直接作用于列(Column)的向量化函数:

  • week_of_year_with_default_mode:单参数(日期列),内部以week_mode(0)调用compute_week,对应 SQL 中的WEEK(date)
  • week_of_year_with_mode:双参数(日期列 + mode 列),对应WEEK(date, mode),因此 mode 也可以来自表字段而非仅限常量。

上述两种入口的对应测试分别位于 time_functions_test.cpp(weekWithDefaultModeTest)与 time_functions_test.cpp(weekWithModeTest)。后者逐行验证了 2007-01-01、2017-05-01、2020-09-23、2015-10-11、2014-12-11、2001-05-03、2005-02-03、2003-09-03 在 mode 3/2/1/0/5/7/4/6 下的周号,可作为实现正确性的直接佐证。

周边相关函数与选择建议

在周维度分析中,建议按如下方式选择函数:

需求推荐写法说明
对齐 MySQL 默认行为WEEK(date)等价WEEK(date, 0),周日起始、0–53
符合 ISO 8601 标准周WEEK(date, 3)周一起始、1–53,财务/跨国业务常用
直接取 ISO 周号WEEKOFYEAR(date)等价WEEK(date, 3)
需要"年+周"拼接键YEARWEEK(date)返回YYYYWW,避免跨年周与年份错位
周粒度聚合分组键DATE_TRUNC(date, week)截断到所在周起始日(周一 00:00:00)

需要特别注意:不要将WEEK(date, 0)(默认)的结果直接与 ISO 周号混用。默认模式以周日为起点,而大多数"周报"约定周一为起点,两种口径下同一日期可能相差 1 周;跨年日期在 1–53 模式下还会回退到 52/53,务必结合YEARWEEK()或分组时同时输出年份字段,避免"2026 年第 0 周"这类歧义数据进入报表。

总结

  • WEEK(date[, mode])自 v2.3 起可用,行为与 MySQLWEEK()完全一致,返回INT型周序号,非法/空日期返回NULL
  • 8 种 mode 由"周起始日(周日/周一)+范围(0–53/1–53)+第 1 周判定(含周日/含周一/含 4 天以上)"三个维度组合而成,mode=3即 ISO 标准周;
  • 跨年边界日期的行为最为关键:同一2007-01-01在不同 mode 下分别返回 0、1、53,深刻理解这一点才能避免周统计口径错误;
  • 实现层面,BE 端由week_of_year_with_default_mode/week_of_year_with_mode调用week_mode()compute_week()完成计算,相关逻辑与测试用例见 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 3:01:56

BabelDOC:开源 PDF 文献翻译工具,论文双语对照一键生成

BabelDOC&#xff1a;开源 PDF 文献翻译工具&#xff0c;论文双语对照一键生成 【免费下载链接】BabelDOC Yet Another Document Translator 项目地址: https://gitcode.com/GitHub_Trending/ba/BabelDOC BabelDOC 是一款开源的 PDF 文档翻译工具&#xff0c;能把英文论…

作者头像 李华
网站建设 2026/9/18 3:00:51

SQL Server数据库实例从入门到排查:概念、连接与实战配置

/* 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 3:00:50

关于 TheAgentCompany 的 Bash 结论,TaoToken 发 Key 跑 APEX-Agents

/* 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 3:00:27

HTTP与HTTPS核心差异:从信任链到TLS握手实战排障

“HTTP和HTTPS到底有什么区别&#xff1f;”这个问题我在各种场合被问到过不下几百次&#xff1a;面试应届生、帮同事排查线上故障、给测试组讲压测脚本、甚至教家里做电商运营的朋友理解为什么浏览器会提示“不安全”。大多数人第一反应是“HTTPS比HTTP多个S&#xff0c;更安全…

作者头像 李华
网站建设 2026/9/18 3:00:07

91 页报告说开源权重模型只差 4 个月,TaoToken 帮 DeepSeek 调用记账

/* 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 3:00:02

把 OpenClaw 的模型 Base URL 改到 TaoToken 的 API 地址,Gateway 路由照旧

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

作者头像 李华