VARCHAR字段传数字不加引号会导致隐式类型转换,破坏B+树索引有序性,使优化器放弃索引而全表扫描;正确做法是显式加单引号并统一字段与值的类型。

WHERE里VARCHAR字段传数字不加引号,索引直接失效
这是最常见也最容易被忽略的场景:字段是VARCHAR,但SQL里写成WHERE phone = 13800138000。MySQL会把每行phone字符串都转成数字再比对——相当于在索引列上套了CAST(),B+树的有序性彻底作废,优化器只能选type: ALL。
实操建议:
- 所有
VARCHAR/CHAR字段的查询条件,必须显式用单引号包裹,例如WHERE phone = '13800138000' - MyBatis等ORM中,一律用
#{}传参,禁用${}拼接(后者会丢引号) - 上线前对关键查询跑一遍
EXPLAIN,重点盯key是否为NULL、rows是否异常高
INT字段传字符串带引号,同样触发隐式转换
反向情况一样危险:字段是INT,但SQL写了WHERE id = '123'。MySQL会把整型列逐行转成字符串比较,虽然语义等价,但执行计划里仍出现CONVERT(id, CHAR),索引照样失效。
实操建议:
- 字段类型和查询值类型必须严格一致:
INT就传数字字面量,VARCHAR就传带引号字符串 - 别在应用层做“字符串→数字”预处理后再拼SQL;应由驱动或ORM保障类型传递准确
- 用
SHOW CREATE TABLE users确认字段真实类型,别只信Java实体类或文档注释
CAST()或CONVERT()作用于列名,索引必然失效
看到索引失效,第一反应常是“我手动转一下”,比如WHERE CAST(phone AS UNSIGNED) = 13800138000。这反而更糟——函数落在索引列上,MySQL完全无法下推查找,仍是全表扫描。
能走索引的强制转换,只发生在「转换不作用在列上」时:
- ✅ 正确:
WHERE phone = CAST(13800138000 AS CHAR(11))或WHERE phone = CONVERT(13800138000, CHAR) - ❌ 错误:
WHERE CAST(phone AS CHAR) = '13800138000'(函数包列名) - ⚠️ 注意:
CAST(x AS CHAR)默认长度是1,必须显式写CHAR(11),否则可能截断或补空格引发新问题
JOIN时关联字段字符集或排序规则不一致
两张表JOIN,若一边是utf8mb4_unicode_ci,另一边是utf8mb4_general_ci,或字符集干脆一个是utf8mb4、一个是utf8,MySQL无法直接用索引匹配,会退化为嵌套循环+逐行临时转换,性能雪崩。
实操建议:
- 建表时统一字符集与排序规则,推荐
utf8mb4+utf8mb4_unicode_520_ci -
ALTER TABLE修改现有表时,务必同时改CHARACTER SET和COLLATE,不能只改其一 - 用
SHOW FULL COLUMNS FROM table_name核对关联字段的实际Collation值
EXPLAIN里的rows从100跳到100万。


















