按周分组必须绑定iso年份,postgresql用to_char(date, 'iyyy-iw')或extract(isoyear/isoweek)联合分组,sql server用datepart(isoyy/isowk),否则跨年日期如2023-12-31与2024-01-01会被错误分组,导致统计偏差高达30%。

PostgreSQL 和 SQL Server 的按周分组不能只取周数,必须同时绑定 ISO 年份,否则 2023-12-31 和 2024-01-01 这类跨年日期会被错分到不同组,统计偏差可能高达 30%。
PostgreSQL 必须用 TO_CHAR(date, 'IYYY-IW') 或 EXTRACT 组合
直接 GROUP BY EXTRACT(WEEK FROM created_at) 是错的:它只返回 1–53 的纯数字,不带年份,2023-W1 和 2024-W1 会混在一起。而 EXTRACT(ISOWEEK FROM created_at) 虽然返回 ISO 周数,但依然需要对应 ISO 年才能准确定位。
-
TO_CHAR(created_at, 'IYYY-IW')是最简方案,输出如'2024-01',自动对齐 ISO 标准(2023-12-31 →'2024-01') - 若需语义更清晰,用
EXTRACT(ISOYEAR FROM created_at)和EXTRACT(ISOWEEK FROM created_at)两个字段联合GROUP BY - 别用
date_trunc('week', created_at)做分组键:不同 PG 版本对“周一截断”的实现有差异(v12 vs v15),且结果带时间戳精度,容易因小数秒导致重复分组 - 时区不一致会放大错误——确保会话
timezone与业务一致,或显式写created_at AT TIME ZONE 'Asia/Shanghai'
SQL Server 必须同时用 DATEPART(isoyy, date) 和 DATEPART(isowk, date)
DATEPART(week, date) 默认按周日为起点、第1周含1月1日,和 ISO 完全不兼容。2024-01-01(周一)可能被算作 2024 年第 1 周,但 2023-12-31(周日)却被归入 2023 年第 53 周——而它们本该同属 2024 年第 1 周。
- 强制使用
DATEPART(isowk, order_time)(ISO 周数)+DATEPART(isoyy, order_time)(ISO 年),二者缺一不可 - 别用
YEAR(order_time)替代isoyy:2024-12-30 的YEAR()是 2024,但它的 ISO 年是 2025;硬拼会把数据甩到错误年份 - 低版本 SQL Server(isoyy/
isowk,得手动推导 ISO 年,例如:DATEPART(YEAR, DATEADD(day, 26 - DATEPART(ISOWEEK, date), date)) - 执行前建议加
SET DATEFIRST 1,避免会话级设置干扰isowk行为
跨库共性陷阱:NULL、非法时间、时区漂移
所有数据库里,只要日期字段存在 NULL、'0000-00-00' 或超出范围的时间(如 '9999-99-99'),这些记录在 GROUP BY 后就彻底消失,不会计入任何一组——总数凭空变少。
- 务必在
WHERE中显式过滤:WHERE created_at IS NOT NULL AND created_at >= '2000-01-01' - 字符串类型日期(如
'2024/03/15')必须先转类型,PostgreSQL 用created_at::date,SQL Server 用TRY_CONVERT(DATE, created_at),否则函数返回NULL - 如果原始时间存的是 UTC,但业务按北京时间统计,PostgreSQL 需
AT TIME ZONE 'Asia/Shanghai',SQL Server 需AT TIME ZONE 'UTC' AT TIME ZONE 'China Standard Time'
真正难的不是写出第一版 SQL,而是确认那条“2023-12-31”的记录,到底落在哪一组——它决定了整个周统计是否可信。这个点一旦出错,后续所有同比、环比、趋势图全都会偏移。











