WHERE子句中对索引列使用函数或表达式会导致索引失效,因索引按原始值存储,优化器无法映射函数结果;应改用范围查询等等价写法。

WHERE 条件用了函数或表达式
MySQL 在 WHERE 子句中对索引列使用函数(如 UPPER()、DATE()、CONCAT())或计算(如 col + 1、col * 2),会导致该列无法走索引。因为索引是按原始值排序存储的,优化器无法将函数结果直接映射到 B+ 树结构上。
-
SELECT * FROM users WHERE UPPER(name) = 'ALICE';→name索引失效 -
SELECT * FROM orders WHERE DATE(created_at) = '2024-01-01';→created_at索引失效 - 改法:用范围替代函数,比如
created_at >= '2024-01-01' AND created_at - 若必须大小写不敏感,可建函数索引(MySQL 8.0.13+):
CREATE INDEX idx_name_upper ON users ((UPPER(name)));
LIKE 查询以通配符开头
LIKE 模糊匹配时,如果模式以 % 开头(如 LIKE '%abc' 或 LIKE '%abc%'),MySQL 无法利用索引的有序性做快速定位,只能全表扫描。
-
SELECT * FROM products WHERE name LIKE '%phone';→ 索引失效 -
SELECT * FROM products WHERE name LIKE 'phone%';→ 可走索引(最左前缀匹配) - 注意:即使有复合索引
(category, name),WHERE name LIKE '%x'仍不走索引,因为没用到最左列category - 替代方案:全文索引(
FULLTEXT)或倒排表/ES 同步
隐式类型转换导致索引失效
当查询条件中索引列与传入值类型不一致,MySQL 会自动做隐式转换。若转换发生在索引列上(如字符串索引列和数字比较),索引就失效了。
-
name是VARCHAR类型且有索引,但写了WHERE name = 123→ MySQL 转成WHERE CAST(name AS DOUBLE) = 123,索引失效 -
id是INT主键,但传入字符串WHERE id = '123'→ 通常不失效(数字列转字符串不影响索引),但存在风险,尤其在字符集不同或带空格时 - 检查方法:用
EXPLAIN看type是否为ALL,再看Extra是否含Using where; Using index或Using filesort - 统一参数类型,应用层传参前校验并强转
OR 条件中部分字段无索引
MySQL 对 OR 的处理较保守:只要 OR 任意一边的字段没有索引,整个条件就可能放弃使用索引(尤其在旧版本中)。
-
SELECT * FROM users WHERE status = 1 OR city = 'Beijing';→ 若只有status有索引,city没索引,则大概率全表扫描 - 改法一:给
city加索引;改法二:拆成UNION(需注意去重开销):SELECT ... WHERE status = 1 UNION SELECT ... WHERE city = 'Beijing' - 注意:MySQL 8.0+ 对某些简单
OR有优化,但仍不可依赖;IN通常比OR更友好(如WHERE status IN (1,2)可走索引) - 复合索引下,
OR很难命中多个字段,不如用覆盖索引 + 应用层合并
真正难排查的是“看似能走索引却没走”的情况——比如统计信息过期、索引选择性太低(如 gender 列只有两个值)、或者优化器预估走索引比全表扫描还慢。这时候 EXPLAIN FORMAT=TREE 和 ANALYZE TABLE 比盲目加索引更有用。


















