mysql按周统计必须用yearweek(dt,1)并配合年份维度,避免%y-%u错误;按季度须同时group by year()和quarter();跨数据库需适配函数,大表应建生成列索引。

直接按 WEEK() 或 QUARTER() 分组,90% 的情况会跨年错乱或索引失效——必须同时带上年份维度,并避开字符串拼接陷阱。
MySQL 按周统计:别用 %Y-%u,改用 YEARWEEK(date, 1)
常见错误是写成 DATE_FORMAT(dt, '%Y-%u'),比如把 '2024-12-30'(周一)算成 '2024-01',实际它属于 2025 年第 1 周。结果就是年底数据全挤进下一年开头。
- 正确写法:
GROUP BY YEARWEEK(dt, 1),参数1表示 ISO 标准(周一为每周第一天) - 想还原成当周周一日期?用
STR_TO_DATE(CONCAT(YEARWEEK(dt, 1), ' Monday'), '%X%V %W') - 如果字段是字符串(如
'2024/03/15'),先转类型:STR_TO_DATE(dt, '%Y/%m/%d'),否则YEARWEEK()返回NULL - 性能注意:
YEARWEEK(dt, 1)无法走dt字段的索引,大表建议加生成列:ADD COLUMN yw INT GENERATED ALWAYS AS (YEARWEEK(dt, 1)) STORED,再建索引
MySQL 按季度统计:必须 YEAR() 和 QUARTER() 同时分组
只写 GROUP BY QUARTER(order_date),2023-Q1 和 2024-Q1 就会合并成一行,报表直接崩掉。
- 稳定写法:
GROUP BY YEAR(order_date), QUARTER(order_date) - 输出格式想好看点?用
CONCAT(YEAR(order_date), '-Q', QUARTER(order_date)),但别在GROUP BY里直接拼,先分组再格式化 - 字段是字符串?比如
'20230615',得先STR_TO_DATE(order_date, '%Y%m%d')转成日期再取季度 - 别用
SUBSTR(order_date, 1, 4)截年份——它不校验合法性,'abc'也会返回'ab',查不出错但结果不可信
PostgreSQL / SQL Server 用户别硬套 MySQL 函数
WEEK()、YEARWEEK()、QUARTER() 在 PostgreSQL 和 SQL Server 里根本不存在,硬抄会报错。
- PostgreSQL 按 ISO 周:用
to_char(dt, 'IYYYIW')(输出如'202401'),和 MySQL 的YEARWEEK(dt, 1)行为一致 - PostgreSQL 按季度:用
EXTRACT(YEAR FROM dt)::int和EXTRACT(QUARTER FROM dt)::int,显式转整型避免小数精度问题(如4.0) - SQL Server 按周:用
DATEPART(isowk, dt)+DATEPART(year, DATEADD(day, 3 - DATEPART(weekday, dt), dt))模拟 ISO 周年 - SQL Server 按季度:必须同时写
DATEPART(YEAR, dt)和DATEPART(QUARTER, dt),别用DATEPART(yy, dt)(两位年份,2001 和 2021 都变01)
补全空周期和性能优化:别让 GROUP BY 毁掉索引
业务报表常要求“没数据也要显示 0”,而 GROUP BY 天然跳过空周期;更麻烦的是,函数包裹时间字段会让索引完全失效。
- 补周:MySQL 没原生
GENERATE_SERIES,可用递归 CTE 或应用层补;PostgreSQL 直接用GENERATE_SERIES(CURRENT_DATE - '11 weeks'::interval, CURRENT_DATE, '1 week')左连聚合结果 - 性能救命方案:不用
GROUP BY DATE_FORMAT(created_at, '%Y-%m'),改用范围查询 + 应用层拼月份,或加生成列并建索引 - 测试边界值必须包含:
'2023-12-31'(2024 年第 1 周)、'2024-01-01'(2024 年第 1 周)、'2024-12-30'(2025 年第 1 周)——这些点一错,全年周统计全偏










