mysql周统计应统一用yearweek(date_col, 1)或date_format(date_col, '%x%v'),因默认周日起点易致跨年错位;postgresql推荐to_char(date_col, 'iyyy-iw'),sql server须用datepart(isoyy, )与datepart(isowk, )组合,且必须避免仅用week()或year()+week()拼接。

MySQL里用YEARWEEK()和DATE_FORMAT()做周统计,注意周起始日
MySQL默认把周当作“周日到周六”,但业务常要“周一到周日”或“周一到周五”。直接用YEARWEEK(date_col)可能错位——比如2024-01-01是周一,YEARWEEK('2024-01-01')返回202401,但若按周一为起点才合理。更稳妥的是DATE_FORMAT(date_col, '%Y-%u')(%u按周一为每周第一天),或者显式加偏移:YEARWEEK(date_col, 1)(第二个参数1表示周一为周首)。
常见错误:用WEEK()函数不带模式参数,结果依赖MySQL的default_week_format系统变量,导致迁移环境后分组错乱。
- 统计每周订单数:
SELECT DATE_FORMAT(created_at, '%Y-%u') AS week, COUNT(*) FROM orders GROUP BY week; - 想显示“2024-W01”格式:用
CONCAT(YEAR(created_at), '-W', LPAD(WEEK(created_at, 1), 2, '0')) - 跨年周(如2023-12-31可能属于2024年第1周)必须用
YEARWEEK(..., 1),不能只取YEAR()拼接
PostgreSQL用EXTRACT()处理周和月,别硬套MySQL写法
PostgreSQL没有DATE_FORMAT(),得靠EXTRACT()配合TO_CHAR()。按月统计最简:用TO_CHAR(created_at, 'YYYY-MM');按周则推荐TO_CHAR(created_at, 'IYYY-IW')——IYYY是ISO年,IW是ISO周,天然支持跨年周(如2023-12-31 → 2024-01),不用自己算偏移。
容易踩的坑:EXTRACT(WEEK FROM ...)在PostgreSQL里返回的是“年内第几周”(1–53),不带年份,直接GROUP BY EXTRACT(WEEK FROM ...)会把2023-W1和2024-W1混在一起。
- 安全的周分组:
SELECT TO_CHAR(created_at, 'IYYY-IW') AS week, COUNT(*) FROM orders GROUP BY week; - 按自然月(不截断):
SELECT DATE_TRUNC('month', created_at) AS month_start, COUNT(*) FROM orders GROUP BY month_start; -
DATE_TRUNC('week', ...)默认以周日为起点,要改周一就写created_at - INTERVAL '1 day' * EXTRACT(DOW FROM created_at)::int + INTERVAL '1 day'再DATE_TRUNC——太绕,不如用TO_CHAR(..., 'IYYY-IW')
SQL Server的DATEPART()对周敏感,DATEFIRST必须设对
SQL Server默认DATEFIRST是7(周日为第一天),但国内习惯周一。如果没改设置,DATEPART(week, ...)和DATEPART(year, ...)组合会出错:2024-01-01(周一)被算作2024年第1周,但2023-12-31(周日)就算作2023年第53周——而它实际属于2024年的ISO第一周。
解决方案不是硬记逻辑,而是统一用ISO标准:DATEPART(isowk, ...)和DATEPART(isoyy, ...),它们无视DATEFIRST,且自动处理跨年。
- ISO周统计:
SELECT CAST(DATEPART(isoyy, created_at) AS VARCHAR) + '-W' + RIGHT('0' + CAST(DATEPART(isowk, created_at) AS VARCHAR), 2) AS week, COUNT(*) FROM orders GROUP BY DATEPART(isoyy, created_at), DATEPART(isowk, created_at); - 避免
DATEPART(year, ...)+DATEPART(week, ...)组合——除非你确认SET DATEFIRST 1已执行且不会被连接池重置 - 按月统计推荐
DATEFROMPARTS(YEAR(created_at), MONTH(created_at), 1)生成月初日期,比字符串拼接更利于索引利用
跨数据库兼容写法几乎不存在,按目标库选函数是唯一靠谱路径
试图用strftime('%Y-%m', date_col)(SQLite)去适配MySQL或PostgreSQL,只会触发语法错误。各库的日期函数设计哲学不同:MySQL重字符串格式化,PostgreSQL重类型截断,SQL Server重part提取。强行抽象一层(比如用ORM的func.date_trunc)往往掩盖细节,反而在跨年周、时区、索引失效等问题上翻车。
真正要检查的不是“怎么写”,而是“分组边界是否符合业务定义”:财务月是自然月还是结算月?周报里的“本周”是从今天倒推7天,还是固定周一到周日?这些语义必须先对齐,函数只是实现工具。
最常被忽略的点:时间字段是否带时区?2024-01-01 00:00:00+08和2024-01-01 00:00:00+00在按天分组时可能落入不同日期——按周/月更敏感。务必确认字段类型是TIMESTAMP WITH TIME ZONE还是DATE,并在查询中显式AT TIME ZONE或CAST对齐。











