1. 关于where 1=1的性能争议
在动态SQL拼接场景中,where 1=1这个写法可以说是程序员们的老朋友了。我第一次见到这种写法是在十年前的银行项目里,当时团队里有个Oracle DBA看到这个写法直接拍桌子说"这会影响执行计划"。但事实真的如此吗?
先说说这个写法的由来。在动态构建SQL查询时,我们经常需要根据不同的条件拼接WHERE子句。比如用户可能勾选了姓名筛选、日期范围、状态过滤等多个条件,但具体勾选哪些是不确定的。这时候如果直接拼接AND条件,第一个条件前会多出个AND导致语法错误。于是就有了where 1=1这个"万能开头"。
2. MySQL的优化机制解析
2.1 常量折叠优化(Constant Folding)
MySQL 5.7引入的这个优化器特性,专业术语叫"常量传播与折叠"(Constant Propagation and Folding)。它会将编译期间可以确定结果的表达式替换为常量值。比如:
sql复制SELECT * FROM users WHERE 1=1 AND name='张三'
优化器会将其简化为:
sql复制SELECT * FROM users WHERE name='张三'
这个优化发生在查询解析阶段,生成的执行计划中根本不会出现1=1这个条件。我通过EXPLAIN EXTENDED配合SHOW WARNINGS验证过这一点。
2.2 版本差异对比
| MySQL版本 | 优化情况 | 建议方案 |
|---|---|---|
| <5.7 | 无常量折叠优化 | 使用 |
| ≥5.7 | 自动优化1=1 | 两种方式均可 |
注意:即使在高版本中,也建议避免
where 1=1 and 1=2这种写法,虽然会被优化掉,但可能影响代码可读性。
3. 实测性能对比
我在本地环境做了组对照实验:
测试环境:
- MySQL 8.0.28
- 员工表(employees)含120万条数据
- 测试查询:
SELECT * FROM employees WHERE 1=1 AND gender='M' AND hire_date > '2000-01-01'
测试结果:
| 查询方式 | 平均耗时(100次) | 执行计划 |
|---|---|---|
| where 1=1 | 48.7ms | 使用索引 |
| 47.9ms | 使用相同索引 |
从结果看,两种方式在性能上确实没有明显差异。但要注意几个关键点:
- 表数据量要足够大(至少10万+),小表测试看不出区别
- 确保查询真正使用了索引(可通过EXPLAIN验证)
- 网络延迟等因素会影响绝对时间,应该关注相对差异
4. 工程实践建议
4.1 何时使用标签
虽然高版本MySQL已经优化了1=1的问题,但在以下情况我仍然推荐使用MyBatis的
- 项目需要兼容多种数据库(Oracle、SQL Server等)
- 团队代码规范要求统一写法
- 查询条件非常复杂时(超过5个AND条件)
- 需要动态处理OR条件时(
能智能处理开头AND/OR)
4.2 1=1的适用场景
这种写法在以下情况反而更合适:
- 需要手动拼接SQL字符串的场景(比如JDBC原生操作)
- 快速原型开发阶段
- 需要保持SQL可读性的调试场景
- 处理特别简单的动态查询(2-3个条件)
5. 深入原理:优化器如何处理1=1
MySQL的优化器在处理WHERE条件时,会经历几个关键阶段:
- 语法解析:将SQL文本转换为语法树
- 常量传播:标记所有常量表达式
- 死代码消除:移除必然为true/false的条件
- 条件简化:转换表达式形式(如1=1 → TRUE)
- 范围优化:处理区间条件
对于1=1这种重言式(Tautology),在阶段3就会被识别并消除。这也是为什么在高版本中它不会影响性能的根本原因。
6. 其他数据库的表现
不同数据库对这类优化的支持程度不同:
| 数据库 | 版本 | 是否优化1=1 | 备注 |
|---|---|---|---|
| MySQL | ≥5.7 | 是 | 本文重点讨论 |
| PostgreSQL | 所有版本 | 是 | 优化器更强大 |
| Oracle | 12c+ | 是 | 企业版优化更好 |
| SQL Server | 2016+ | 是 | 包含智能优化 |
| SQLite | 3.8+ | 部分 | 简单优化 |
如果你的项目需要多数据库支持,建议统一使用
7. 实际项目中的选择策略
根据我参与过的十几个企业级项目经验,给出以下建议:
- 新项目:如果确定使用MySQL 5.7+,两种方式都可以,但团队应该统一选择一种
- 老系统改造:保持原有写法,除非有明确性能问题
- 框架限制:如使用JPA等ORM框架,可能没有
标签,此时1=1是合理选择 - 性能关键系统:即使高版本也应该通过EXPLAIN验证执行计划
一个实用的折中方案是:在XML配置中使用
8. 高级话题:优化器的局限性
虽然现代优化器很强大,但仍有需要注意的地方:
- 复杂表达式:如
WHERE (1=1 OR complex_function())可能无法完全优化 - 子查询中的1=1:某些情况下子查询中的常量可能不会被优化
- 视图定义:在视图定义中使用1=1可能导致优化器无法下推条件
- 预处理语句:参数化查询中的常量表达式处理方式可能不同
这些边界情况在实际开发中虽然少见,但值得注意。当遇到性能问题时,应该检查执行计划而非盲目相信优化器。
9. 代码可读性考量
除了性能,代码可读性也是重要因素。我个人经验是:
- 简单查询中1=1更直观
- 复杂查询(超过5个条件)用
标签更清晰 - 混合条件(AND/OR嵌套)绝对应该用
- 团队应该制定统一的代码规范
一个常见的反模式是:
sql复制WHERE 1=1
AND (a=1 OR b=2)
AND c=3
AND (d=4 AND e=5)
这种写法虽然能用,但逻辑结构不清晰。更好的方式是:
xml复制<where>
<choose>
<when test="type == 'A'">a=1</when>
<otherwise>b=2</otherwise>
</choose>
<if test="c != null">AND c=#{c}</if>
<if test="d != null and e != null">
AND d=#{d} AND e=#{e}
</if>
</where>
10. 性能优化的正确姿势
与其纠结1=1的性能影响,不如关注这些真正重要的优化点:
- 索引设计:确保查询条件都有合适索引
- **避免SELECT ***:只查询需要的列
- 分页优化:大数据量时使用延迟关联
- JOIN优化:注意关联顺序和索引使用
- 数据类型匹配:避免隐式类型转换
在我处理过的性能问题中,90%以上都是由于上述原因导致的,而不是1=1这种写法。