只要让查询值类型和字段类型严格一致,90%的隐式转换索引失效即可解决;如VARCHAR字段需加引号、INT字段禁用字符串、JOIN字段统一类型与字符集、存储过程变量声明对齐字段类型,并禁用${}拼接与动态CONCAT。
直接改SQL语句就能修好——只要让查询值类型和字段类型严格一致,90%的隐式转换索引失效当场消失。
这是线上最常见、杀伤力最强的写法。phone是VARCHAR(11),却写成WHERE phone = 13800138000,MySQL被迫对每行phone做CAST(phone AS UNSIGNED),B+树跳查能力彻底归零。
EXPLAIN显示type: ALL、key: NULL、rows等于全表行数WHERE phone = '13800138000',单引号不可省${}拼接(如WHERE phone = ${phone}),它会吃掉引号;必须用#{}
反向操作同样危险。id是BIGINT,却写成WHERE id = '123',MySQL仍需逐行调用CONVERT(id, CHAR),旧版本或统计信息不准时,EXPLAIN可能显示type: index而非ref,实际仍是全表扫描。
WHERE id = 123
CAST(id AS CHAR) = '123'兜底——函数作用于列,索引照样失效SELECT @@sql_mode确认是否启用STRICT_TRANS_TABLES,它能让部分隐式转换直接报错,暴露问题两张表用code关联,但table_a.code是VARCHAR(20)且字符集utf8mb4_unicode_ci,table_b.code是BIGINT或字符集utf8,MySQL无法直接利用索引做等值匹配,退化为嵌套循环+逐行转换。
SHOW CREATE TABLE table_a和SHOW CREATE TABLE table_b,对比CHARSET、COLLATE、字段类型ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
ON条件中显式转换字面量侧,如ON a.code = CAST(b.code AS CHAR(20)),而非ON CAST(a.code AS SIGNED) = b.code
ALTER TABLE可能锁表,生产环境需评估窗口期存储过程中DECLARE v_phone VARCHAR(11),但调用时SET v_phone = 13800138000(没加引号),或用SELECT phone INTO v_id FROM users把字符串赋给数值型变量,都会触发运行时隐式转换,优化器在编译阶段无法确定转换方向,最终把CAST()压到字段上执行。
phone是VARCHAR(11) → v_phone也必须是VARCHAR(11)
CONCAT('WHERE phone = ', v_phone);正确写法是CONCAT("WHERE phone = '", v_phone, "'")
SET @sql = "SELECT * FROM users WHERE phone = ?"; PREPARE stmt FROM @sql; EXECUTE stmt USING v_phone;
EXPLAIN,必须抽出来单独测试;执行完后立刻SHOW WARNINGS,看是否有Warning 1739类提示真正难的不是写出正确SQL,而是让所有环节——从API参数校验、ORM配置、存储过程变量声明,到DBA建表规范——都守住“类型严格对齐”这一条线。漏掉任意一环,一个没加的引号就能让索引形同虚设。