“range checked for each record”表示mysql未找到可用索引,对前表每行都临时评估索引适用性,属执行计划降级;主因是隐式类型转换(如bigint字段传字符串)、字符集/排序规则不一致、复合索引未遵最左前缀、统计信息过期或函数滥用。

Range checked for each record 是什么信号
这行提示不是警告,而是 MySQL 优化器明确告诉你:它没找到能直接用的索引,只能退而求其次,在连接前先“猜”一遍哪些索引可能有用。它出现在 EXPLAIN 的 Extra 列里,本质是执行计划降级的表现——本该走索引快速定位,现在变成对每行都临时评估索引适用性。
最常见触发原因:隐式类型转换
当 pn.receive_cust_id = '100000000000365' 这类条件出现时,如果 receive_cust_id 字段定义为 BIGINT,但传入的是字符串字面量,MySQL 就会强制做类型转换。此时即使该字段有索引,也会因类型不匹配导致索引失效,key 列为空,type 变成 ALL 或 index,并伴随 Range checked for each record。
- 检查字段类型:
SHOW COLUMNS FROM spot_procurement_invitation LIKE 'receive_cust_id'; - 确认参数类型:应用层传参是否加了引号(如 Java 中
String.valueOf(id)而非Long直接绑定) - 验证转换影响:手动执行
EXPLAIN SELECT * FROM spot_procurement_invitation WHERE receive_cust_id = 100000000000365;(不带引号),对比key是否出现
其他容易被忽略的诱因
除了类型转换,以下情况也会触发该提示,且往往更隐蔽:
-
JOIN条件中关联字段字符集或排序规则不一致,比如一边是utf8mb4_0900_as_cs,另一边是utf8mb4_general_ci - 复合索引未按最左前缀使用,例如索引是
(a, b, c),但查询只用了WHERE b = ? AND c = ? - 统计信息严重过期,
ANALYZE TABLE spot_procurement_invitation;后再EXPLAIN可能消失 - 查询中混用函数,如
WHERE DATE(create_time) = '2024-01-01',导致索引无法下推
为什么在 Navicat 里特别容易误判
Navicat 的图形化执行计划界面本身不暴露隐式转换细节,你只看到 Range checked for each record 和高 rows 值,却看不到背后的类型 coercion。更麻烦的是,v16 及更早版本在同步模式下执行 EXPLAIN 时,若服务端响应慢,UI 会卡死,让你误以为“计划生成失败”,其实只是网络阻塞了结果返回。
真正要定位问题,必须绕过 Navicat 界面,用命令行或 MySQL Workbench 直连,执行 EXPLAIN FORMAT=JSON,重点看 query_block.nested_loop 下各表的 using_join_buffer 和 access_type,才能确认是不是因为前表结果未确定,导致后表索引无法预判。











