MySQL在WHERE中对索引列使用YEAR()、DATE()等函数会导致索引失效,因B+树仅存储原始值,无法映射函数结果,只能全表扫描;应改写为范围查询,如YEAR(create_time)=2025→create_time>='2025-01-01' AND create_time<'2026-01-01'。
WHERE里用YEAR()、DATE()等函数导致全表扫描
这类写法会让索引完全失效,mysql无法将函数结果映射回索引有序结构,只能退化为type=all。比如where year(create_time) = 2025,哪怕create_time上有索引也白搭。
实操建议:
- 把函数从字段上拿掉,改写成范围条件:
WHERE create_time >= '2025-01-01' AND create_time - 对
DATE()同理:WHERE DATE(create_time) = '2025-03-15'→WHERE create_time >= '2025-03-15 00:00:00' AND create_time - 如果业务必须按年/月聚合,考虑在应用层或视图中预计算
year、month字段并单独建索引,而不是每次查都算
WHERE中对索引字段做运算(如+1、*2、SUBSTR)
运算会破坏索引的有序性,优化器无法推导出对应索引位置。例如WHERE age + 1 > 30,即使age有索引,也会触发全表扫描。
实操建议:
- 把运算移到等号右边:
WHERE age > 29替代WHERE age + 1 > 30 -
WHERE id * 2 = 100→WHERE id = 50 -
WHERE SUBSTR(name, 1, 3) = 'abc'→WHERE name LIKE 'abc%'(前提是能接受前缀匹配语义) - 若必须截取中间段(如
SUBSTR(name, 5, 2)),索引基本无解,得考虑生成计算列并建索引(MySQL 5.7+支持)
隐式类型转换让索引失效
常见于字段是VARCHAR但WHERE里传了数字,比如WHERE user_id = 123而user_id是字符串类型。MySQL会把每行user_id转成数字再比,索引失效。
实操建议:
- 确认字段类型和传入值类型严格一致:
WHERE user_id = '123'(如果user_id是VARCHAR) - 在Navicat执行计划里看
key是否为空、type是否变成ALL或index,这是最直接的线索 - 用
SHOW CREATE TABLE table_name核对字段定义,别依赖Navicat表设计界面显示的“类型”——它有时不准确
LIKE带前置通配符(%abc)无法走索引
WHERE name LIKE '%abc'必然全表扫描;WHERE name LIKE 'abc%'可以走索引,但WHERE name LIKE '%abc%'不行。
实操建议:
- 能用前缀匹配就别用全模糊:
WHERE name LIKE 'John%'比WHERE name LIKE '%John%'快得多 - 真要全文检索,别硬扛LIKE,改用
FULLTEXT索引 +MATCH ... AGAINST - 小数据量临时查可用,但上线前必须检查执行计划里的
rows——如果接近表总行数,说明已经失控
真正卡住人的往往不是“要不要加索引”,而是WHERE条件一变形,索引就悄悄失效了。Navicat里点一下“解释”,重点盯key和rows两列,比读十篇教程都管用。


















