MySQL中WEEK()函数需显式指定mode=1(周一为起点)才符合国内习惯,否则2024-01-01会被误判为第53周;跨年统计应改用YEARWEEK()或统一用TRUNC(...,'IW')等算术方式推导周一日期以确保一致性。

MySQL中用WEEK()函数按周统计订单数量,注意mode参数决定周一/周日为起点
WEEK(date) 默认以周日为一周起点(mode=0),但国内习惯以周一为始,直接写WEEK(order_time)会导致分组错位。必须显式指定mode=1(周一为第一日,且一年中至少有4天在该周内才视为第1周)或mode=2(周一为始,第1周从1月1日所在周一算起)。
常见错误是没加mode,导致2024-01-01(周一)被算进第53周而非第1周;或者用mode=0时,周日订单被归入下一周。
-
WEEK(order_time, 1)更符合中国业务习惯,推荐作为默认选择 - 若需兼容ISO标准(
YEARWEEK(order_time, 1)),应改用YEARWEEK()避免跨年周混淆 - WEEK()返回纯数字(如1~53),无法区分不同年份的“第5周”,跨年统计必须搭配
YEAR()或YEARWEEK()
Oracle和PostgreSQL中用TRUNC(date, 'IW')对齐ISO周,比'WW'更可靠
TRUNC(order_time, 'IW')会将日期截断到当周周一(ISO标准),例如2024-01-01(周一)→ 2024-01-01,2024-01-06(周六)→ 2024-01-01。它不依赖NLS设置,也不受1月1日星期几影响,比'WW'(按日历周,1月1日所在周恒为第1周)稳定得多。
典型误用是写TRUNC(order_time, 'WW'):2023-12-31(周日)会被截成2023-12-31所在周的周日(即2023-12-31),而2024-01-01(周一)却被截成2024-01-01所在周的周日(2024-01-07),同一自然周被拆到两个分组。
- Oracle/PG中优先用
TRUNC(order_time, 'IW'),再按该字段GROUP BY - 若需显示“2024-W01”格式,可用
TO_CHAR(order_time, 'IYYY-"W"IW')(Oracle)或TO_CHAR(order_time, 'IYYY-"W"IW')(PG) - 注意
'IW'在Oracle中要求数据库字符集支持,极少数旧库需检查NLS_TERRITORY
跨数据库可移植方案:用DATE_SUB或GENERATE_SERIES构造周起始日
当SQL要同时跑在MySQL、PostgreSQL甚至SQL Server上时,硬写WEEK()或TRUNC(..., 'IW')必然报错。稳妥做法是统一用算术方式推导周一日期:MySQL用DATE_SUB(order_time, INTERVAL WEEKDAY(order_time) DAY),PostgreSQL用order_time - (EXTRACT(DOW FROM order_time)::int - 1) % 7,SQL Server用DATEADD(day, 2-DATEPART(weekday, order_time), order_time)。
这个思路本质是“把任意日期回退到本周一”,绕过各数据库对“周定义”的分歧。缺点是无法利用索引加速——如果订单表很大,建议提前在ETL层加一列week_start_date并建索引。
- MySQL中
WEEKDAY()返回0=周一,所以减去该值即可回到周一 - PostgreSQL中
EXTRACT(DOW FROM ...)返回0=周日,需转成1=周一再模7防负数 - 所有方案都需配合
CAST(... AS DATE)或DATE(...)确保类型一致,避免时间部分干扰分组
统计结果出现空周?补全逻辑不能只靠LEFT JOIN生成日期序列
按周GROUP BY后,某几周没订单就自然消失,业务方常要求补0。但简单LEFT JOIN一个预生成的周序列表,容易漏掉边界:比如订单最早是2024-03-01,但需求要从2024-01-01开始补全12周——序列表若只覆盖实际订单范围,就会缺前两月。
更麻烦的是周定义不一致导致JOIN失败:序列表用TRUNC(d, 'IW')生成周一,而主表用WEEK(d,1),两者对2024-01-01的计算结果可能不同(前者是2024-01-01,后者是2024年第1周),JOIN不上。
- 补全前先统一分组字段:主表和序列表都用相同逻辑算周起始日(如全部用
TRUNC(..., 'IW')) - 序列生成范围必须显式指定,例如“从订单最早日期往前推2周,到最后日期往后推2周”
- MySQL 8.0+可用
WITH RECURSIVE生成序列,但要注意递归深度限制,默认1000,超量需调大cte_max_recursion_depth
真实场景里,最易被忽略的是跨年周的语义一致性——比如2023-12-31这天,在按自然周、ISO周、财务周三种口径下,可能分别属于2023年第52周、2024年第1周、2023财年第13周。选错函数或mode,报表数字就对不上,而且很难排查。










