date(created_at)分组会变慢,因为函数导致索引失效,优化器被迫全表扫描并逐行计算日期;应改用时间范围过滤(如created_at >= '2026-07-21' and created_at
直接用
DATE()做GROUP BY会强制全表扫描,根本原因是函数使索引失效,不是语法错,是执行计划崩了。为什么
DATE(created_at)分组会变慢MySQL 和 PostgreSQL 都无法对
DATE(created_at)这类函数结果直接利用created_at上的索引。优化器看到函数,就放弃走索引路径,转而全表扫描每行、逐行计算日期值,再分组——数据量一过百万,耗时直线上升。
- 哪怕
created_at字段有 B-tree 索引,GROUP BY DATE(created_at)也完全用不上- 分组键变成字符串形式的日期(如
'2026-07-21'),内部还要做类型转换和哈希计算- 如果配合
HAVING或多层嵌套,执行计划更容易退化成临时表 + 文件排序用时间范围替代
DATE()函数把“按天分组”转化为“按时间区间过滤”,让 WHERE 条件命中索引,再聚合,效率提升最稳。
iSlide PPT下载一款AI演示文稿工具,主要用于DeepSeek AI加持,输入主题生成专业PPT,支持Word/PDF等45种文档导入,职场汇报、教学提案轻松搞定,适合需要提升相关任务效率的用户。
- 不要写:
GROUP BY DATE(created_at)- 改写为:先确定日期范围,用
created_at >= '2026-07-21' AND created_at 等条件提前过滤- 若需多天统计,用
UNION ALL拆成多个带索引的单日查询,比单条函数分组快得多- PostgreSQL 可结合
generate_series()构造日期桶,再 LEFT JOIN,避免全表扫用
created_at::date(PostgreSQL)或DATE(created_at)加函数索引仅当必须保留函数写法时才考虑——但必须配对应索引,否则毫无意义。
- PostgreSQL:执行
CREATE INDEX idx_orders_date ON orders ((created_at::date));,注意括号写法,这是函数索引- MySQL 不支持函数索引(8.0+ 支持,但只限于存储生成列),更推荐走范围方式
- 函数索引不会自动被优化器选用,务必用
EXPLAIN验证是否生效;出现Index Scan using idx_orders_date才算成功- 函数索引增大写开销,且需定期
VACUUM(PG)或ANALYZE更新统计信息真正要警惕的隐形陷阱
很多人以为加了索引就万事大吉,但实际中容易忽略三点:
- WHERE 条件里混用
DATE(created_at) = '2026-07-21'—— 这依然走不了索引,得改成范围写法- 视图里封装了
DATE(created_at)分组,外层再加 WHERE,优化器大概率无法下推,导致先全量聚合再过滤- 分区表按
created_at分区,但查询用DATE(created_at),分区裁剪会失效












