WEEK函数在MySQL中返回指定日期所在周的序号,具体数值取决于mode参数:mode=0以周日为起点且首周至少含1天,mode=1以周一为起点且首周需含4天以上;默认mode=0,不遵循ISO标准。

WEEK函数在MySQL里返回的是什么?别直接当周数用
WEEK() 返回的数字取决于模式(mode)参数,比如 WEEK('2024-01-01', 0) 和 WEEK('2024-01-01', 1) 可能差1周——前者把周日当一周起点,后者周一才是起点。更麻烦的是,WEEK() 对年初/年末的跨年周处理不一致:2023-12-31 可能算作第53周,也可能算作第1周,全看 mode。
- 默认 mode 是 0,按周日开始、第一周至少含1天就算(ISO标准不适用)
- 要和
YEARWEEK()对齐,必须显式指定 mode=1(周一为始,第一周需含4天以上才算) - 直接用
WEEK(created_at)分组,可能把同一天用户拆到两个“周”里(比如跨年时)
统计每周新增用户,得先定义“周”的边界
新增用户指首次出现的 user_id,不能简单按注册时间截取周粒度——得先找出每个用户最早的 created_at,再按周归类。
- 先用
GROUP BY user_id找最小时间:MIN(created_at) - 再用
YEARWEEK(min_time, 1)统一生成周标识(返回形如 202401 的整数,避免跨年歧义) - 别用
WEEK()单独配合YEAR()拼接,因为YEAR('2023-12-31')是 2023,但WEEK('2023-12-31', 1)可能是 1 → 实际属于 2024 年第1周
SELECT YEARWEEK(first_login, 1) AS week_id, COUNT(*) AS new_users FROM ( SELECT user_id, MIN(created_at) AS first_login FROM users GROUP BY user_id ) t GROUP BY week_id ORDER BY week_id;
PostgreSQL 或 SQL Server 用户注意:没有 WEEK 函数
PostgreSQL 用 EXTRACT(WEEK FROM created_at),但它默认按 ISO 标准(周一为始,第一周含当年周四),和 MySQL 的 WEEK(col, 1) 行为基本一致;但 EXTRACT(YEAR FROM created_at) 和周不联动,跨年周仍需用 EXTRACT(ISOYEAR FROM ...) 配合。
- SQL Server 没有原生 WEEK,得用
DATEPART(week, created_at),但它的周从周日开始,且不自动处理跨年——得自己用DATEPART(iso_week, ...)+YEAR(...)拼 ISO 周标识 - SQLite 完全没 WEEK 支持,得靠
strftime('%Y-%W', created_at),但%W把周一定为每周第一天,且第0周存在,容易漏掉1月1日是周一的情况
按周统计后,日期对不上怎么办?
常见现象:查出 week_id = 202401,但不知道对应哪7天。别硬算——加一列用 STR_TO_DATE(CONCAT(week_id, ' Monday'), '%X%V %W')(MySQL)或 TO_DATE(week_id::text, 'IYYYIW')(PostgreSQL)转成当周周一。
-
YEARWEEK()返回值不是连续整数(比如 202452 后是 202501),不能用week_id + 1算下周 - 如果要做周环比,得用窗口函数或自连接,按
week_id排序后取前一行,而不是假设数值+1就是上周
跨年周和时区偏移是隐形坑,尤其当 created_at 是 TIMESTAMP 类型且数据库时区和业务时区不一致时,同一用户在不同时区下可能被分到不同周。先确认 created_at 存的是 UTC 还是本地时间,再决定是否要 CONVERT_TZ() 或 AT TIME ZONE 标准化。


















