索引失效是因为字段侧隐式转换导致b+树无法使用,如varchar字段用数字查询、字符集不一致join、orm参数类型错配等,必须保证字段与值类型严格一致才能命中索引。

因为数据库必须对每一行字段值做运行时转换,索引完全失效,退化为全表扫描。
WHERE里字符串字段用数字查询,为什么索引直接废了
MySQL 或 PostgreSQL 遇到 WHERE phone = 13800138000(phone 是 VARCHAR)时,不会把右侧数字转成字符串去匹配,而是把左侧每行 phone 值都解析成数字再比——B+ 树索引无法支持这种逐行计算,只能扫全表。
- 现象:
EXPLAIN显示type=ALL、rows等于表总行数、key=NULL - 正确写法必须是
WHERE phone = '13800138000',让两边都是字符串,索引才可命中 - 别信“字符串转数字代价小就没事”——只要转换发生在字段侧(即左边),索引一律失效
字符集不一致也会触发隐式转换
两个表 JOIN 或子查询中,一边是 utf8mb4,另一边是 utf8(哪怕只是排序规则不同),MySQL 就会在连接时对字段做隐式字符集转换,同样绕过索引。
- 检查方式:
SHOW CREATE TABLE t1和SHOW CREATE TABLE t2对比CHARSET和COLLATE - 修复不是加
CONVERT,而是统一改表:ALTER TABLE t1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 临时视图或 CTE 中混用不同字符集字段,也会在物化时触发转换,执行计划里会出现
Using where; Using temporary
ORM 和参数绑定最容易悄悄踩坑
Java 的 PreparedStatement.setString(1, "123") 查 INT 字段、Node.js 的 req.query.id 直接拼进 SQL、Golang 的 pgx.Conn.QueryRow(..., "123") ——这些都不是“传字符串”,而是让数据库被迫做隐式转换。
- Java 应该用
ps.setInt(1, 123);Node.js 必须parseInt(req.query.id)再传;Golang 要传int64或显式pgtype.Int4 - MyBatis 的
#{id}如果 Java 类型是Long,但 DB 字段是VARCHAR,照样触发转换——类型定义必须端到端对齐 - 上线前快速核对:查
DESCRIBE table_name确认字段类型,再看参数来源是不是字符串,如果是,立刻加类型转换
最麻烦的不是报错,而是没报错却慢得离谱;最隐蔽的不是 WHERE 条件,而是 JOIN 字段、视图定义、甚至函数索引里的表达式字段类型错配。每次加新查询,先盯住字段类型和传入值类型是否字节级一致,比调优更管用。











