函数索引在 WHERE DATE(create_time) = ‘2025-01-01’ 的应用

13次阅读

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’ 的应用

直接说结论:WHERE date(create_time) = '2025-01-01' 这种写法几乎必然导致索引失效,即使 create_time 上建了 B-tree 索引。

为什么 DATE() 会绕过索引

mysql(及大多数主流数据库)无法对函数包裹的列直接使用索引扫描。因为 DATE(create_time) 是一个运行时计算值,优化器无法将它映射回索引中已排序的原始时间戳范围。

常见错误现象包括:执行计划中 type 显示 ALL(全表扫描),keyNULL,哪怕表有百万行也慢得明显。

  • 索引只对原始列值有序,不存储 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_timeDATETIMEtimestamp,右边也用字符串格式;如果是 int 存秒级时间戳,就用数字比较
  • 这种写法在 create_time 有索引的前提下,EXPLaiNtype 通常是 rangekey 显示实际使用的索引名

什么时候可以考虑函数索引

仅当业务逻辑强依赖某类固定函数表达式,且查询模式高度统一时,才值得建函数索引(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),优化器依然可能放弃索引。最稳的永远是原始列上的范围表达式。

text=ZqhQzanResources