SQL 为什么有时不走索引?

数据库索引失效主因有四:①索引列参与函数/表达式运算(如UPPER(name));②隐式类型转换(如INT字段传字符串);③低选择性或统计信息过期;④OR条件未优化。应优先用EXPLAIN分析执行计划,针对性改写SQL或调整索引策略。

WHERE 条件里用了函数或表达式

数据库优化器通常无法对索引字段做函数运算后仍使用索引(除非是函数索引且版本支持)。比如 WHERE UPPER(name) = 'TOM'WHERE age + 1 > 25,会导致全表扫描。

实操建议:

  • 把函数移到右侧:改写为 WHERE name = UPPER('tom')(注意大小写语义是否等价)
  • 避免在索引列上做数学运算、拼接、类型转换(如 CONVERT(varchar, id)
  • MySQL 8.0+ / PostgreSQL / Oracle 支持函数索引,但需显式创建,不是默认行为

隐式类型转换让索引失效

当 WHERE 中的列类型和传入值类型不一致,数据库可能自动转换单边数据类型,导致无法命中索引。典型例子:WHERE user_id = '123',而 user_idINT 类型。

实操建议:

  • 检查执行计划中是否有 Type conversionimplicit_cast 提示
  • 确保参数类型与字段定义完全一致(尤其 ORM 自动生成 SQL 时容易出问题)
  • EXPLAINEXPLAIN ANALYZE 观察 keytype 字段是否为空/为 ALL

索引选择性低或统计信息过期

如果某列重复值极高(比如 stat

us 只有 0/1),即使建了索引,优化器也可能认为走索引比全表扫描更慢;另外,大量 INSERT/UPDATE 后未更新统计信息,会让优化器误判数据分布。

实操建议:

  • SELECT COUNT(DISTINCT col)/COUNT(*) FROM tbl 粗略评估选择性(> 5% 较安全)
  • PostgreSQL 执行 ANALYZE table_name;MySQL 8.0+ 可设 innodb_stats_auto_recalc = ON;SQL Server 建议开启自动更新统计信息
  • 对低选择性字段,考虑组合索引中把它放在后面,或改用覆盖索引减少回表

OR 条件未被正确优化

WHERE a = 1 OR b = 2 很难同时利用两个单列索引,尤其当两列无复合索引时,多数引擎会退化为全表扫描或仅用其中一个索引(取决于代价估算)。

实操建议:

  • 拆成 UNION ALL(SELECT ... WHERE a = 1) UNION ALL (SELECT ... WHERE b = 2 AND a != 1)
  • 建立包含两列的复合索引(但要注意最左前缀原则是否满足查询模式)
  • 某些场景下改用 INEXISTS 更易走索引,需结合具体数据量测试

真正卡住人的,往往不是“有没有建索引”,而是“为什么建了却不走”——这背后几乎总是执行计划误判或 SQL 写法触发了隐式规则。每次遇到,先看 EXPLAIN 输出里的 keyExtra 字段,比反复重建索引管用得多。