mysql按月分组用date_format(date_col, '%y-%m'),按周分组推荐yearweek(date_col, 1)(周一为周首);postgresql用date_trunc()更可靠;sql server需注意datepart周逻辑及iso周标准。

MySQL 中用 YEARWEEK() 和 DATE_FORMAT() 实现按周/按月分组
MySQL 没有原生的“按自然周”或“按日历月”聚合函数,直接 GROUP BY date_col 会按天分组。关键在于把日期转成统一的周期标识——比如把 2024-03-15 和 2024-03-18 都映射到同一周(如 202409),或同一月(如 '2024-03')。
常用组合:
- 按月:用
DATE_FORMAT(date_col, '%Y-%m')—— 返回字符串'2024-03',能正确跨年、排序也自然 - 按周(周一为每周开始):用
YEARWEEK(date_col, 1)—— 第二个参数1表示周一为周首、周数从 1 开始;返回整数如202409 - 按周(周日为每周开始):用
YEARWEEK(date_col, 0)—— 注意这会导致 2024-01-01(周一)被归入第 53 周(属于 2023 年),容易出错
示例:
SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, COUNT(*) FROM orders GROUP BY month;
PostgreSQL 中用 DATE_TRUNC() 最稳妥
PostgreSQL 的 DATE_TRUNC('month', date_col) 直接返回当月第一天的时间戳(如 '2024-03-01 00:00:00'),DATE_TRUNC('week', date_col) 默认以周日为起点截断到周日零点。它比字符串拼接更可靠,且支持索引(如果字段上有函数索引)。
注意点:
-
DATE_TRUNC('week', ...)总是截到**周日**,如果你业务要求“周一为周首”,得手动偏移:DATE_TRUNC('week', date_col + INTERVAL '1 day') - INTERVAL '1 day' - 别用
EXTRACT(YEAR FROM ...) || '-' || EXTRACT(MONTH FROM ...)拼字符串——月份不补零(如'2024-3'),排序和比较会错乱 - 按周聚合时,
DATE_TRUNC('week', ...)返回的是时间戳,不是字符串,GROUP BY 结果天然可排序
SQL Server 里小心 DATEPART() 的周计算逻辑
SQL Server 的 DATEPART(week, date_col) 返回的是“一年中的第几周”,但它的起始日和算法依赖 SET DATEFIRST 设置(默认是 7=周日)。更安全的做法是组合 YEAR() 和 DATEPART(iso_week, ...) 来模拟 ISO 周标准(周一为周首,第 1 周含当年第一个周四)。
按月推荐写法:
GROUP BY YEAR(date_col), MONTH(date_col),但要注意:这样分组后无法直接用
ORDER BY 排出连续月份(因为 YEAR/MONTH 是两个字段),建议改用:GROUP BY DATEFROMPARTS(YEAR(date_col), MONTH(date_col), 1)(SQL Server 2012+),返回每月第一天的日期值,排序、展示、关联都方便。
常见坑:
- 只用
DATEPART(week, ...)分组,会导致 2023-12-31 和 2024-01-01 被分到不同“周”,即使它们在同一个 ISO 周里 -
CONVERT(char(7), date_col, 120)可以截出'2024-03',但char(7)在某些版本可能隐式截断,不如FORMAT(date_col, 'yyyy-MM')清晰(不过FORMAT性能较差)
通用提醒:时区、空值和性能
所有数据库中,如果日期字段含时区(如 TIMESTAMP WITH TIME ZONE),先用 AT TIME ZONE 或等效方式转成目标时区再截断,否则跨时区用户看到的“本月”可能错位。
空值(NULL)会被单独分到一组,如果不想统计,加 WHERE date_col IS NOT NULL。
性能上,对原始日期字段做函数操作(如 DATE_FORMAT(created_at, '%Y-%m'))通常无法走索引。如果查询频繁,考虑增加计算列并建索引(MySQL 5.7+ 支持函数索引,PostgreSQL 可建表达式索引,SQL Server 支持持久化计算列)。
真正麻烦的从来不是语法怎么写,而是“这一周到底从哪天算起”“跨年周怎么编号”“用户看到的‘本月’是否和财务月一致”——这些得跟产品、运营对齐,而不是只看 SQL 跑没跑通。










