WHERE DATE(create_time) = ‘2025-01-01’ 必然导致索引失效,因函数计算使优化器无法利用B-tree索引;应改用范围查询:create_time >= ‘2025-01-01 00:00:00’ AND create_time
直接说结论:
WHERE date(create_time) = '2025-01-01'这种写法几乎必然导致索引失效,即使create_time上建了 B-tree 索引。为什么 DATE() 会绕过索引
mysql(及大多数主流数据库)无法对函数包裹的列直接使用索引扫描。因为
DATE(create_time)是一个运行时计算值,优化器无法将它映射回索引中已排序的原始时间戳范围。常见错误现象包括:执行计划中
type显示ALL(全表扫描),key为NULL,哪怕表有百万行也慢得明显。
- 索引只对原始列值有序,不存储
DATE()结果- 优化器无法预估
DATE(create_time) = '2025-01-01'对应哪些原始时间戳区间- 即使你给
create_time加了函数索引(如 MySQL 8.0+ 的INDEX (DATE(create_time))),它也仅适用于该函数形式,且不能用于范围查询或 ORDER BY 优化正确写法:用范围查询代替函数调用
把日期条件转成时间戳范围,让优化器能走索引的 range 扫描。
例如查 2025-01-01 全天的数据:
WHERE create_time >= '2025-01-01 00:00:00' AND create_time < '2025-01-02 00:00:00'
- 必须用
而非,避免跨天重复覆盖- 类型要一致:如果
create_time是DATETIME或timestamp,右边也用字符串格式;如果是int存秒级时间戳,就用数字比较- 这种写法在
create_time有索引的前提下,EXPLaiN中type通常是range,key显示实际使用的索引名什么时候可以考虑函数索引
仅当业务逻辑强依赖某类固定函数表达式,且查询模式高度统一时,才值得建函数索引(MySQL 8.0+ / postgresql 支持)。
比如你**长期、高频、只查**
DATE(create_time)等值匹配,且基本不查其他时间粒度,可建:CREATE INDEX idx_create_date ON t1 (DATE(create_time));
- 该索引对
WHERE DATE(create_time) = '2025-01-01'有效,但对WHERE create_time > '2025-01-01'无效- 它不替代原始列索引,
ORDER BY create_time仍需原索引- 函数索引体积更大、维护成本略高,且无法用于 LIKE 或前缀匹配
真正容易被忽略的是:即便用了函数索引,只要查询里混用不同函数(比如
WHERE YEAR(create_time)=2025 AND MONTH(create_time)=1),优化器依然可能放弃索引。最稳的永远是原始列上的范围表达式。
