GROUP BY WEEK() 在不同数据库中行为不一致:MySQL默认周日为起点,PostgreSQL和SQL Server按周一且受设置影响;应统一用周起始日(如date_trunc或DATE_SUB)分组并排序,避免跨年错位与补零缺失。

GROUP BY WEEK() 函数在不同数据库里的行为差异
MySQL 的 WEEK() 默认按周日开始算,而 PostgreSQL 用 EXTRACT(WEEK FROM ...) 是按周一,SQL Server 的 DATEPART(week, ...) 还受 SET DATEFIRST 影响。直接套用会错位一周,尤其跨年时更明显——比如 2024-12-30(周一)可能被分进第 1 周或第 53 周。
实操建议:
- 优先用
DATE_SUB(date_col, INTERVAL WEEKDAY(date_col) DAY)(MySQL)或date_trunc('week', date_col)(PostgreSQL)生成「周一为起点的周起始日」,再GROUP BY这个计算列 - 避免只写
GROUP BY WEEK(date_col),它不带年份,2023-12-31 和 2024-01-01 可能归到同一个WEEK()值里 - 如果必须用
WEEK(),加上YEARWEEK(date_col, 1)(MySQL),第二个参数1表示周一为周首、ISO 标准,返回带年份的整数(如 202401)
如何让“最近 N 周”结果自动补零(无数据的周也显示)
原始 GROUP BY 只返回有数据的周,但趋势图需要连续横轴。不能靠前端拼接,得在 SQL 层补全日期序列。
实操建议:
- MySQL 8.0+:用
WITH RECURSIVE生成最近 12 周的week_start列表,再LEFT JOIN到你的统计结果上 - PostgreSQL:用
GENERATE_SERIES配合date_trunc('week', ...)构造基准周序列 - 注意时区:
CURRENT_DATE AT TIME ZONE 'Asia/Shanghai'要显式指定,否则服务器默认时区可能导致周边界偏移
WHERE 日期范围过滤必须放在 GROUP BY 之前
常见错误是先 GROUP BY 再 HAVING 过滤日期,比如写 HAVING date_col >= '2024-01-01' —— 这实际无效,因为 date_col 在分组后已不是单值,且 HAVING 是对聚合结果过滤,不是对原始行。
实操建议:
- 所有时间范围约束必须写在
WHERE子句,例如:WHERE date_col BETWEEN '2024-01-01' AND '2024-03-31' - 如果要动态取“最近 90 天”,用
WHERE date_col >= CURRENT_DATE - INTERVAL 89 DAY(MySQL)或WHERE date_col >= CURRENT_DATE - INTERVAL '89 days'(PostgreSQL) - 别在
SELECT里用DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)算起始日再WHERE,这会让索引失效;应提前算好常量传入
统计结果按周序正确排序的关键细节
用 ORDER BY WEEK(date_col) 会把 2023 年最后一周排在 2024 年第一周后面,视觉上断层。真正要的是「周起始日」升序,而不是周编号。
实操建议:
-
ORDER BY MIN(date_col)最稳妥,它天然对应每组的最早日期,即周起始日附近 - 如果用了
date_trunc('week', date_col)或DATE_SUB(...)计算列,就直接ORDER BY那个别名列 - 禁止
ORDER BY WEEK(...), YEAR(...)拼序,容易因跨年逻辑混乱;宁可用ORDER BY week_start_date显式字段
跨年周处理和补零逻辑最容易被忽略,尤其当业务要求“每周一凌晨重置统计”时,date_trunc('week', ...) 类函数的行为必须和业务定义严格对齐。










