在写 SQL 时,很多人喜欢在 WHERE 条件里加上1=1,比如这样:
SELECT * FROM user WHERE 1=1 AND name = 'Tom' AND age > 20;
看起来挺方便对吧?尤其是那种动态拼接 SQL 的代码里,这样写能少写一点判断逻辑。但其实,这种写法有几个隐藏的坑,尤其是在生产环境下,可能带来性能问题、逻辑问题,甚至安全风险。
一、1=1 的由来
很多老项目都是通过字符串拼接来生成 SQL 的,比如 Java 里:
String sql = "SELECT * FROM user WHERE 1=1";
if (name != null) {
sql += " AND name = '" + name + "'";
}
if (age != null) {
sql += " AND age = " + age;
}
这样写最大的“优点”就是不用管是不是第一个条件,反正前面加个AND肯定不会出错。但问题在于,这种偷懒的写法虽然方便,却埋下了不少坑。
二、执行层面的问题
数据库在解析 SQL 时,1=1其实是一个恒为真的表达式,它不会影响结果,但它仍然要被数据库解析、优化、执行。
数据库优化器在执行 SQL 时会建立执行计划(execution plan),而1=1这种无意义的条件会让优化器多做一次逻辑判断。虽然这个判断代价很小,但在高并发系统中,如果每秒执行上万条 SQL,这种“无意义的运算”会累计成可观的开销。
比如下面这两条语句:
SELECT * FROM order WHERE 1=1 AND status = 'PAID';
SELECT * FROM order WHERE status = 'PAID';
执行结果完全一样,但第一条会让优化器多一步逻辑判断。在单条 SQL 里可能差别不明显,但如果在循环里拼接执行几千次,就会变成资源浪费。
三、可维护性问题
1=1虽然能让拼接 SQL 的代码简单点,但当 SQL 越来越复杂,比如带上JOIN、EXISTS、GROUP BY时,这种写法会让语句难以阅读、难以维护。
尤其是当你查日志时,看到类似这样的:
SELECT u.id, u.name, o.amount
FROM user u
LEFT JOIN order o ON u.id = o.user_id
WHERE 1=1 AND u.status = 'active' AND o.create_time > '2024-01-01';
1=1完全没有意义,反而会让人觉得这条语句是拼接出来的,从而怀疑有没有 SQL 注入的风险。
四、安全隐患(SQL 注入)
如果项目中没有使用参数化查询,而是直接拼接字符串(这种情况老项目特别多),1=1很容易和 SQL 注入问题扯上关系。
比如下面的例子:
String sql = "SELECT * FROM user WHERE 1=1";
sql += " AND name = '" + inputName + "'";
如果用户输入Tom' OR 1=1 --拼出来的 SQL 变成:
SELECT * FROM user WHERE 1=1 AND name = 'Tom' OR 1=1 --';
这就等于直接查询了整个表。很多人以为用了1=1只是为了拼接方便,但它在 SQL 注入中其实是攻击者最喜欢的“突破口”。
五、动态 SQL 的正确做法
现代项目一般不会直接拼接 SQL,而是通过框架来安全生成,比如 MyBatis、JPA 等。 以 MyBatis 为例,你可以这样写:
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
MyBatis 的标签会自动去掉多余的AND或OR,根本不需要1=1。 最终生成的 SQL 是干净的、安全的,比如:
SELECT * FROM user WHERE name = 'Tom' AND age = 25;
这才是动态 SQL 的正确姿势。
六、如果实在要拼接怎么办?
如果你真的在写 JDBC 原生代码,建议至少使用StringBuilder加上参数绑定(PreparedStatement),避免直接拼接:
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
List<Object> params = new ArrayList<>();
if (name != null) {
sql.append(" AND name = ?");
params.add(name);
}
if (age != null) {
sql.append(" AND age = ?");
params.add(age);
}
PreparedStatement ps = connection.prepareStatement(sql.toString());
for (int i = 0; i < params.size(); i++) {
ps.setObject(i + 1, params.get(i));
}
ResultSet rs = ps.executeQuery();
虽然这里仍然用了1=1,但至少使用了参数绑定,不会被注入攻击利用。 当然,更好的办法是直接判断第一个条件是否需要WHERE,而不是靠1=1来糊弄过去。
七、总结一下
小结
1=1这种写法在 10 年前确实很常见,因为那时候大家还在手写 SQL 拼接,但现在的框架早就帮你解决了动态拼接的问题。
如果还在项目里看到WHERE 1=1,基本可以判断这段代码是旧时代遗留物。 与其靠1=1去规避逻辑,不如老老实实写条件判断,或者交给框架去拼接。
毕竟,干净的 SQL 不只是为了美观,更是性能、安全和可维护性的基础。