where 1=1 本身不会导致索引失效,mysql 5.0+ 优化器在解析阶段直接剔除它;真正引发索引失效的是后续条件写法不当、参数绑定错误或 join 与 where 混用导致的执行计划退化。

WHERE 1=1 本身不会导致索引失效,MySQL 5.0+ 优化器会在解析阶段直接剔除它;真正让索引“丢失”的,是后续条件写法不当、参数绑定方式错误,或 JOIN + WHERE 混用时的执行计划退化。
WHERE 1=1 在 MySQL 中的真实行为
MySQL 不会为 1=1 生成执行节点,EXPLAIN 或 SHOW WARNINGS 都看不到它。源码级验证显示:在 JOIN::optimize() 阶段,where_cond 被设为 NULL,等价于没写。所以它不参与索引选择逻辑,也不增加计算开销。
但要注意:这个优化只对字面量恒真表达式有效,比如 1=1、'a'='a';而 IF(1,1,0)=1 或带用户变量的表达式,可能逃逸优化,不建议混用。
海量数据下索引“看似失效”的真实原因
当 EXPLAIN 显示 type=ALL 或 key=NULL,问题通常不在 1=1,而在以下几点:
- 动态拼接时,实际生效的条件列未建索引(如只对
name建了索引,但查询用了UPPER(name)) - MyBatis 等框架中使用了
<if></if>但未包裹在<where></where>标签内,导致生成WHERE 1=1 AND ...后又多出一个AND引发语法容忍但语义错位 - 多表
JOIN时,WHERE条件写在驱动表之外,优化器误判过滤时机,放弃使用被驱动表索引 - 参数化查询中传入
NULL,而字段定义允许 NULL,且未加IS NOT NULL限定,导致索引选择率预估失真
避免踩坑的实操建议
不用删掉 1=1,但要让它“不干扰”:
- 在 MyBatis 中,改用
<where></where>标签替代手写WHERE 1=1:<where><if test="name != null">name LIKE #{name}</if><if test="status != null">AND status = #{status}</if></where>它会自动处理首尾AND,且不引入冗余条件 - Java 拼接 SQL 时,用布尔标记控制
WHERE/AND,而非依赖1=1:StringBuilder sql = new StringBuilder("SELECT * FROM t"); List<object> params = new ArrayList(); boolean hasWhere = false; if (name != null) { if (!hasWhere) { sql.append(" WHERE "); hasWhere = true; } else { sql.append(" AND "); } sql.append("name LIKE ?"); params.add("%" + name + "%"); }</object> - 对高频动态查询字段,建立联合索引并按「过滤性高 → 范围查询 → 等值查询」顺序排列,例如:
(status, create_time, user_id),而非(user_id, status, create_time)
验证是否真被优化了
别信经验,看证据:
- 执行
EXPLAIN FORMAT=TREE(MySQL 8.0+)或EXPLAIN EXTENDED; SHOW WARNINGS,确认输出 SQL 中已无1=1 - 开启
optimizer_trace,搜索"optimized_conds"字段,看是否为空或仅剩业务条件 - 对比加/不加
1=1的Handler_read_*状态值(SHOW STATUS LIKE 'Handler%'),二者应完全一致
真正要花时间调的,从来不是 1=1,而是那个漏建索引的 order_no 字段,或者把 DATE(created_at) 写进 WHERE 的凌晨三点的上线脚本。











