正确使用or需注意括号优先级、空值陷阱(用is null而非= null)、避免隐式转换错误;多值等值判断优先用in;or易导致索引失效,可改写为union或cte;混合and/or必须加括号。

OR 连接多个 WHERE 条件的写法
直接在 WHERE 子句中用 OR 连接多个布尔表达式即可,但要注意括号优先级和空值陷阱。
常见错误是写成 WHERE status = 'active' OR 'pending' —— 这实际等价于 WHERE status = 'active' OR TRUE(因为非空字符串在多数 SQL 引擎中被隐式转为 TRUE),导致全表扫描。
- 正确写法必须为每个条件单独写出完整比较:
WHERE status = 'active' OR status = 'pending' - 当涉及不同字段时,注意逻辑分组:
WHERE (type = 'user' AND age > 18) OR (type = 'admin') -
NULL参与比较会返回UNKNOWN,所以status = 'active' OR status IS NULL才能真正捕获空值,status = NULL永远不成立
IN 替代多个 OR 的适用场景
当多个 OR 是对同一字段做等值判断(如 status = 'a' OR status = 'b' OR status = 'c'),优先用 IN —— 更简洁、可读性高,且大多数数据库对此做了优化。
但注意:IN 不能替代跨字段或带运算符的 OR。比如 price > 100 OR category = 'sale' 就没法改写成 IN。
- 简单等值推荐:
WHERE status IN ('active', 'pending', 'draft') - 含
NULL时IN无效(status IN ('a', NULL)不会匹配NULL),仍需显式写status IS NULL - PostgreSQL 和 MySQL 8.0+ 支持
IN子查询;SQLite 对子查询IN有性能限制,大结果集慎用
OR 导致索引失效的典型情况
很多开发者以为加了索引就万事大吉,但 OR 往往让优化器放弃使用索引,尤其是多字段混合条件时。
例如 WHERE user_id = 123 OR order_date > '2024-01-01',即使 user_id 和 order_date 各有单列索引,MySQL 5.7 默认也不会合并使用(需 8.0+ 的 index merge 优化且开启对应选项)。
- 单字段多值尽量走
IN,它更容易命中索引 - 跨字段
OR可考虑改写为UNION(注意去重开销):(SELECT ... WHERE user_id = 123) UNION ALL (SELECT ... WHERE order_date > '2024-01-01' AND user_id != 123) - SQL Server 中可用
OPTION (RECOMPILE)让执行计划适配运行时参数,缓解 OR 带来的参数嗅探问题
WHERE 里混用 AND 和 OR 的坑
没加括号时,AND 优先级高于 OR,这和数学运算规则一致,但容易误判逻辑。
比如 WHERE is_deleted = 0 OR type = 'vip' AND created_at > '2023-01-01' 实际等价于 WHERE is_deleted = 0 OR (type = 'vip' AND created_at > '2023-01-01'),而非你可能想要的 (is_deleted = 0 OR type = 'vip') AND created_at > '2023-01-01'。
- 只要出现混合逻辑,无条件加括号,不要依赖默认优先级
- 复杂条件建议拆成 CTE 或子查询,提升可读性和调试便利性
- 某些 ORM(如 Django ORM 的
|操作符)会自动加括号,但原生 SQL 必须自己控制
真正麻烦的不是语法怎么写,而是当 OR 出现在高频查询的 WHERE 里时,执行计划可能随时退化——尤其在数据分布倾斜或统计信息过期的情况下。上线前务必看 EXPLAIN,别只信语句能跑通。











