DATE_FORMAT分组会导致索引失效、性能下降,应优先使用DATE()或YEAR()+MONTH()组合;按天统计用DATE(create_time)配合范围查询,按月统计推荐GROUP BY YEAR(create_time), MONTH(create_time)。

直接说结论:DATE_FORMAT 能按天/月分组,但不是最优解——它会强制转成字符串,导致索引失效、性能陡降;真要高效统计,优先用 DATE() 或 YEAR() + MONTH() 组合。
为什么 DATE_FORMAT 分组容易出问题
常见现象是加了索引却没走索引,查询变慢几倍甚至几十倍。根本原因是 DATE_FORMAT(<code>create_time, '%Y-%m-%d') 返回的是字符串,MySQL 无法将该表达式与 create_time 上的 B+ 树索引做匹配。
-
create_time是DATETIME类型,有索引 - 写
GROUP BY DATE_FORMAT(create_time, '%Y-%m')→ 索引失效 - 写
GROUP BY DATE(create_time)→ 可命中索引(如果条件中还有WHERE create_time >= '2024-01-01') - 同理,
GROUP BY YEAR(create_time), MONTH(create_time)也能用上索引范围扫描
按天统计的推荐写法
核心是保持分组字段和过滤字段都基于原始时间列做计算,不引入函数包裹列本身(除非是覆盖索引场景)。
- 安全高效:用
DATE(create_time)分组,配合WHERE create_time >= '2024-01-01' AND create_time - 避免隐式转换:别写
WHERE DATE(create_time) = '2024-01-01',这会让索引失效 - 示例:
SELECT DATE(create_time) AS day, COUNT(*) AS cnt FROM orders WHERE create_time >= '2024-01-01' AND create_time
按月统计时 YEAR()+MONTH() 比 DATE_FORMAT 更可靠
按月聚合时,DATE_FORMAT(create_time, '%Y-%m') 看起来简洁,但排序和索引友好性差;而 YEAR(create_time)*100 + MONTH(create_time) 是整数,天然可排序、可走索引,也方便后续程序解析。
- 推荐写法:
GROUP BY YEAR(create_time), MONTH(create_time)(语义清晰,MySQL 优化器识别好) - 进阶写法:
GROUP BY YEAR(create_time)*100 + MONTH(create_time)(结果如 202401、202402,适合导出或下游消费) - 千万别这样:
GROUP BY DATE_FORMAT(create_time, '%Y年%m月')—— 中文字符彻底破坏索引和排序逻辑 - 注意时区:如果业务跨时区,确认
create_time存的是 UTC 还是本地时间,DATE()和YEAR()都受time_zone设置影响
真正难的不是写出能跑的 SQL,而是让这条 SQL 在百万级数据下仍稳定在 100ms 内返回——关键就在分组字段是否“可下推”、是否“可索引”。DATE_FORMAT 是个便利函数,不是分组设计的默认选项。











