应分行列写WHERE条件、避免CASE WHEN入WHERE、优先用EXISTS替代IN、布尔字段统一用数值比较;每个逻辑单元独占一行并缩进对齐,OR条件须括号包裹且单独成组,IN子查询需防NULL,布尔字段命名宜用status等业务词而非is_开头。

WHERE子句里堆条件容易出错,别硬塞
多个布尔条件直接用 AND 连在一行,比如 WHERE status = 'active' AND type IN ('user', 'admin') AND created_at > '2024-01-01' AND deleted = 0,看着紧凑,实则难改、难调试、易漏括号。尤其加了 OR 后,优先级一乱,结果就偏了。
真正可读的做法是:每个逻辑单元独占一行,用缩进对齐操作符,把主外键关联放最上,业务过滤往下排。
- 用换行和空格替代“挤在一起”,让
=、IN、>垂直对齐 -
OR条件必须用括号包裹,且单独成组,例如(type = 'user' OR type = 'admin') - 避免
NOT (a AND b)这类嵌套否定,拆成a = 0 OR b = 0更直观
CASE WHEN不是万能的,别在WHERE里硬套
CASE WHEN 适合在 SELECT 中做值映射或分类统计,但放进 WHERE 会严重拖慢查询——它迫使数据库对每行都执行表达式计算,无法利用索引。
真要实现“多条件分支过滤”,优先考虑:
- 用
UNION ALL拆成多个明确路径(比如不同状态走不同索引),再合并结果 - 把动态逻辑提到应用层判断,SQL只负责固定条件查询
- 若必须用
CASE,确保它出现在SELECT或ORDER BY,而非WHERE
IN() 和 EXISTS() 的语义差异常被忽略
WHERE id IN (1, 2, 3) 看似简洁,但遇到子查询时:WHERE id IN (SELECT user_id FROM logs WHERE action = 'login'),如果子查询返回 NULL,整条 IN 判断直接为 UNKNOWN,该行被过滤掉——这不是 bug,是 SQL 三值逻辑的必然结果。
更稳的选择是:
- 用
EXISTS替代IN子查询,它不关心值是否为NULL,只判断是否存在匹配行 - 若坚持用
IN,显式排除空值:WHERE id IN (SELECT user_id FROM logs WHERE action = 'login' AND user_id IS NOT NULL) -
IN列表超过 500 项时,MySQL 可能触发优化器降级,建议分批或改用临时表
布尔字段命名和取值要一致
写 WHERE is_deleted = 0 或 WHERE is_deleted = false 看似一样,但在不同数据库里行为可能分裂:PostgreSQL 把 false 当布尔字面量,MySQL 会把它转成整数 0,而 SQL Server 对 bit 类型只认 0/1。
统一做法是:
- 字段名别用
is_开头,改用业务动词,如status(值为'active'/'archived') - 如果必须用布尔字段,建表时明确类型:
status BOOLEAN DEFAULT FALSE(PostgreSQL)、status TINYINT(1) DEFAULT 0(MySQL) - 查询时始终用数值比较:
WHERE status = 1,不依赖隐式转换
最麻烦的从来不是怎么写对,而是别人半年后读你那行 WHERE a=1 AND b!=0 OR c IS NULL AND d LIKE '%x%' 时,得花十分钟画真值表才能确认逻辑是否真如注释所说。

















