函数作用于索引列会导致MySQL无法使用B+树索引,必须全表扫描;如DATE(create_time)、YEAR()、UPPER()等出现在WHERE左侧时均会失效索引,优化器因此选择ALL访问类型。

函数作用于索引列时,MySQL无法做索引树查找
因为B+树索引存储的是字段的原始值,不是函数计算后的结果。当你写 WHERE DATE(create_time) = '2025-04-18',MySQL必须对每一行的 create_time 执行 DATE() 计算,再比对结果——这等于放弃了索引的有序结构,只能逐行扫描。
优化器看到这种模式,直接判定:走索引要先查索引页、再回表取值、再算函数、再过滤,成本远高于顺序读整张表,于是主动选 type: ALL。
哪些函数/表达式会触发这个问题
只要出现在 WHERE 左侧(即作用于索引列),以下写法都等效于“废掉索引”:
-
YEAR(create_time)、MONTH(create_time)、DATE(create_time) -
UPPER(name)、LOWER(email)、SUBSTR(title, 1, 5) -
age + 1 > 20、price * 1.1 -
CONCAT('abc', code)、TRIM(name)
注意:LIKE 'abc%' 不在此列——它没对列做函数操作,只是前缀匹配,仍可走索引。
怎么改写才能继续用索引
核心原则:把函数从索引列“挪到常量侧”,让条件变成可下推到索引树的范围或等值判断。
- 时间函数 → 拆成闭区间:
WHERE create_time >= '2025-04-18 00:00:00' AND create_time - 字符串大小写 → 提前统一存储格式,或加函数索引(MySQL 8.0+):
CREATE INDEX idx_name_upper ON users ((UPPER(name))) - 数值运算 → 反向解方程:
WHERE price * 1.1 改为 <code>WHERE price
别依赖 FORCE INDEX 强制走索引——如果底层数据分布导致回表开销爆炸,强制反而更慢。
容易被忽略的隐性函数调用
有些“看起来没函数”的写法,其实触发了隐式转换,效果和显式函数一样:
-
WHERE phone = 13800138000(phone是VARCHAR)→ MySQL 自动转成CAST(phone AS SIGNED) -
WHERE create_time = '2025-04-18'(create_time是DATETIME)→ 字符串被转为日期,但精度丢失,可能无法命中索引最左前缀 - ORM 自动生成的
WHERE id IN (?, ?, ?)中,若传入类型与字段不一致(如BIGINT字段传String),也会触发隐式转换
真正关键的不是“有没有建索引”,而是执行计划里 key 是否非空、key_len 是否符合预期、Extra 里有没有 Using where; Using index ——这些才是索引是否生效的铁证。


















