对索引列使用函数(如upper、date)会导致索引失效,因b+树存储原始值;mysql 8.0+支持函数索引(如create index idx_upper_name on users ((upper(name))))解决该问题,但需确保函数确定性且语法正确。

WHERE 条件里对索引列用函数会直接让索引失效
MySQL 在执行查询时,如果 WHERE 子句中对已建索引的列施加了函数(比如 UPPER()、DATE()、SUBSTRING()),优化器通常无法使用该索引进行快速定位,转而走全表扫描。
这不是 MySQL “不够聪明”,而是索引 B+ 树里存的是原始值,不是函数计算后的结果。数据库没法靠索引结构反推“哪些原始值经过 UPPER() 后等于 'ABC'”。
- 常见错误写法:
WHERE UPPER(name) = 'JOHN'(哪怕name有索引) - 正确做法:确保索引列以“裸值”形式出现在比较左侧,函数尽量挪到右侧或提前计算好
- 若业务必须大小写不敏感匹配,优先考虑建函数索引(MySQL 8.0+)或使用带排序规则的列(如
utf8mb4_0900_as_cs)
MySQL 8.0+ 可用函数索引绕过部分限制
从 MySQL 8.0 开始支持在表达式上建索引,这是真正解决“索引列运算”问题的正解,但要注意语法和适用边界。
它不是给任意函数都建索引,而是把函数结果物化进索引树。所以只适用于确定性、无副作用的函数。
- 合法示例:
CREATE INDEX idx_upper_name ON users ((UPPER(name))) - 非法示例:
CREATE INDEX idx_rand ON t ((RAND()))(非确定性函数不允许) - 注意括号写法:双括号
((UPPER(name)))是语法必需,漏掉外层括号会报错 - 查询时仍需写成
WHERE UPPER(name) = 'JOHN',优化器才能命中该函数索引
日期字段别用 DATE(created_at) 做条件
对 DATETIME 或 TIMESTAMP 列用 DATE() 提取日期,是索引失效高频场景,尤其在分页、统计类查询中。
本质是把范围查找退化成了等值+函数调用,丢失了索引的有序性优势。
- 错误写法:
WHERE DATE(created_at) = '2024-05-01' - 推荐改写:
WHERE created_at >= '2024-05-01' AND created_at - 如果频繁按天查,可额外加一个生成列
created_date DATE AS (DATE(created_at))并为其建索引(MySQL 5.7+ 支持) - 避免用
BETWEEN写日期范围,容易因时间精度引发边界遗漏
隐式类型转换也会悄悄干掉索引
表面没函数,但字符串和数字混用、字符集不一致、甚至 JSON 字段里取值,都可能触发隐式转换,导致索引失效——这点比显式函数更难察觉。
典型表现是 EXPLAIN 显示 type=ALL 或 key=NULL,但 SQL 看着完全没问题。
- 危险组合:
WHERE user_id = '123'(user_id是INT类型)→ MySQL 会把每行user_id转成字符串再比,索引失效 - JSON 字段陷阱:
WHERE JSON_EXTRACT(profile, '$.age') > 18→ 即使加了虚拟列索引,也要确认是否用了CAST(... AS UNSIGNED)显式转类型 - 排查手段:用
SHOW WARNINGS看优化器重写后的语句,常能看到CONVERT(... USING ...)这类提示










