唯一有效解法是直接改字段或值类型使join、where、子查询两边严格一致;需比对data_type、character_maximum_length、collation_name、is_nullable四者,任一不一致即触发隐式转换导致索引失效。

直接改字段类型或值类型,让 JOIN、WHERE、子查询两边严格一致——这是唯一有效解法。加索引、调参数、换写法,全是在给隐式转换擦屁股。
JOIN 字段类型不一致:type=ALL 且 key=NULL 就是它
只要 ON 两边字段类型不同,比如 users.id 是 INT 而 orders.user_id 是 VARCHAR,数据库就得逐行做类型转换,索引彻底失效。
- 查真实定义:
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, COLLATION_NAME, IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN ('users', 'orders') AND COLUMN_NAME = 'id' - 四个字段必须全对齐:
DATA_TYPE、CHARACTER_MAXIMUM_LENGTH(对字符串)、COLLATION_NAME、IS_NULLABLE - 别信“都是数字”——
VARCHAR(50)和INT在 MySQL 里就是两类,utf8mb4_0900_as_cs和utf8mb4_bin也互不兼容 - 改表用
ALTER TABLE orders MODIFY user_id INT(MySQL)或ALTER TABLE orders ALTER COLUMN user_id TYPE INTEGER USING user_id::INTEGER(PostgreSQL),只加COLLATE到ON条件里无效
WHERE 中字符串字段配数字字面量:EXPLAIN 显示 type=ALL 就是它
phone 是 VARCHAR,你写 WHERE phone = 13812345678,MySQL 会把整列转成数字再比,索引白建。
- 正确写法永远是:
WHERE phone = '13812345678'—— 字符串字段,值必须加引号 - ORM 或应用层传参时也要对齐:MyBatis 里用
jdbcType=VARCHAR,Java JDBC 用setString()而非setInt()给字符串字段赋值 - 验证方法:
SHOW WARNINGS会暴露CAST(phone AS UNSIGNED)这类痕迹;EXPLAIN的Extra列如果只有Using where(没带Using index),基本就是它
子查询返回类型污染外层索引:IN 慢得离谱就查这个
外层 orders.user_id 是 INT,子查询 SELECT id FROM users 却因中间用了 CONCAT() 或 IFNULL() 返回了 VARCHAR,整个外层索引就废了。
- 删掉子查询里所有包裹主键的函数:
SELECT CONCAT(id, '')→ 改成SELECT id - 必须强制转换时,写在子查询
SELECT列上:SELECT CAST(id AS SIGNED) FROM users,别在外部WHERE orders.user_id = CAST(...) - 优先用
EXISTS替代IN:WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = orders.user_id AND u.status = 'active')更稳 - 检查子查询是否含
DISTINCT、聚合或窗口函数——这些会让优化器放弃下推外层索引
视图和 JSON 字段里的隐式转换:看不见的性能杀手
视图里写 created_at::DATE,外部查 WHERE created_date = '2024-01-01' 就没法下推到基表索引;JSON 字段里 JSON_EXTRACT(extra_info, '$.age') = '25' 也会触发全表扫描。
- 视图尽量保留原始类型,上层查询自己控制条件:
WHERE created_at >= '2024-01-01' AND created_at - JSON 查询必须和函数索引表达式完全一致:建的是
INDEX idx_age ON t ((CAST(JSON_EXTRACT(extra_info, '$.age') AS UNSIGNED))),查就得写CAST(JSON_EXTRACT(extra_info, '$.age') AS UNSIGNED) = 25 -
UNION ALL各分支列类型必须显式对齐,否则数据库会悄悄升格为高精度类型,导致中间结果膨胀、排序变慢
最麻烦的不是发现隐式转换,而是它藏在 ORM 自动生成 SQL 或前端传参拼接里——语句能跑通,一上量就卡死。上线前对所有高频查询跑一遍 EXPLAIN,比事后救火省力十倍。











