MySQL优化器会自动重排WHERE中的等值条件以匹配联合索引最左前缀,但范围条件、OR及缺失最左列会导致索引失效;动态SQL需确保必查字段不被跳过,避免隐式转换。

WHERE 条件顺序颠倒不影响索引命中,是因为 MySQL 查询优化器会重排条件,但前提是联合索引定义顺序本身合理。
MySQL 优化器真会自动重排 WHERE 条件
优化器在生成执行计划前,会把 WHERE 中的等值条件(=、IN)按联合索引定义的列顺序重新组织。它不关心你 SQL 里先写 age = 25 还是 name = '张三',只看哪些列出现在查询中、是否满足最左前缀。
- 索引是
INDEX idx_name_age (name, age),哪怕你写WHERE age = 25 AND name = '张三',EXPLAIN的key_len仍显示用了 2 列 - 优化器内部等价转换为
WHERE name = '张三' AND age = 25,才能定位 B+ 树路径 - 这个重排仅对等值条件可靠;
>、BETWEEN等范围条件不会被“挪到前面”,它们一旦出现,右侧列就失效
动态 SQL 拼接时最容易踩的坑不是顺序,而是缺失最左列
ORM(如 MyBatis)拼 WHERE 时,常因 <if> 判断跳过某些字段,导致实际执行的 SQL 缺失联合索引最左列——这时无论你怎么调换剩下字段的顺序,索引都完全失效。
- 索引
INDEX idx_status_city (status, city),但动态 SQL 只拼出WHERE city = '杭州'→ 全表扫描 - MyBatis 中
<if test="status != null">AND status = #{status}</if>被跳过,只剩city条件,idx_status_city彻底作废 - 解决办法不是“固定拼接顺序”,而是确保高频必查字段(如
status)在逻辑上不可跳过,或单独建INDEX idx_city (city)
OR 条件会让优化器放弃重排,直接绕过联合索引
当动态 SQL 里混入 OR(比如 WHERE status = 'paid' OR user_id = 123),优化器无法构造连续索引扫描路径,即使两个字段都在同一联合索引里,也会大概率退化为全表扫描或 index_merge(不稳定且低效)。
- 联合索引
(status, user_id)对WHERE status = 'paid' OR user_id = 123基本无效 - 想走索引,必须拆成
UNION ALL,且每个子句都满足最左前缀:WHERE status = 'paid'和WHERE user_id = 123 AND status IS NOT NULL(后者需额外单列索引支持) - 动态 SQL 中
OR很难预测,建议从业务层规避,改用多条语句或缓存兜底
真正决定索引能不能用的,从来不是你拼 SQL 时字段写的先后,而是联合索引定义里哪一列在最左、有没有被查询条件覆盖、以及有没有隐式类型转换或范围条件意外截断后续列——这些细节在动态拼接时反而更难肉眼察觉。


















