or连接where条件不是真正并集,因执行计划、null处理和索引使用与union不同;需显式处理null、加括号控制优先级,数据量差异大时宜用union all替代。

SQL中用OR连接多个WHERE条件时,为什么结果不是预期的并集?
直接用 OR 拼接条件(比如 WHERE status = 'pending' OR type = 'urgent')确实能返回满足任一条件的行,但它本质是单次扫描下的逻辑判断,并非真正意义上的“并集操作”。很多人误以为它等价于 UNION,但二者在去重、执行计划、NULL 处理上完全不同。
常见错误现象:OR 条件下加了索引却没走,查询变慢;或字段含 NULL 时漏掉本该匹配的记录(因为 NULL = 'x' 返回 UNKNOWN,不被 OR 视为真)。
- 确保所有参与比较的字段允许
NULL时显式处理,例如改用COALESCE(status, '') = 'pending'或补上IS NULL分支 - 复合条件务必加括号,比如
WHERE (status = 'pending' OR status = 'processing') AND deleted = 0,否则优先级可能出错 - 如果各条件对应的数据量差异极大(如一个条件命中 10 行,另一个命中 10 万行),
OR可能导致优化器放弃索引,此时拆成UNION ALL反而更快
什么时候该用UNION而不是OR?
当你需要明确控制每个子集的结构、排序或去重行为时,UNION 才是真正的并集语义。特别是以下场景:
- 多个查询涉及不同表或不同连接方式,无法用单个
WHERE描述 - 需要对每个分支单独加索引提示或强制使用某索引(
UNION各子句可独立优化) - 要求严格去重且字段类型/长度一致,而
OR在有函数包装或类型隐式转换时可能绕过索引 - 某个条件依赖子查询或窗口函数,不能直接塞进
WHERE中
示例:查“今天创建的用户”和“昨天修改过邮箱的用户”,两个时间字段不同、索引不同,硬套 OR 很容易全表扫:
SELECT id FROM users WHERE created_at >= '2024-06-10' UNION SELECT id FROM users WHERE updated_at BETWEEN '2024-06-09' AND '2024-06-09 23:59:59' AND email_changed = 1
UNION和UNION ALL在多条件并集中的性能差异
UNION 会自动去重并排序,UNION ALL 则原样拼接——这意味着如果你能确定各分支结果无交集(比如按不同状态值分片查询),必须用 UNION ALL。
- 去重成本随结果集增长而陡增,10 万行
UNION可能比UNION ALL慢 3–5 倍 - MySQL 8.0+ 和 PostgreSQL 支持对
UNION ALL子句分别下推条件,但UNION通常会合并执行计划,削弱下推效果 - 如果最终还要
ORDER BY或LIMIT,注意:排序是在并集完成之后才进行的,大数据量时慎用UNION+LIMIT
OR条件下如何让数据库走索引?
不是所有 OR 都能走索引。MySQL 在 5.7+ 对简单等值 OR 有一定优化(index merge union),但 PostgreSQL 默认不合并索引,需依赖 bitmap heap scan。
- 确保每个
OR分支都是独立可用索引的等值条件,避免函数包裹(如UPPER(name) = 'ABC')、计算表达式(如age + 1 > 30) - MySQL 中可尝试强制使用 index merge:
SELECT /*+ USE_INDEX_MERGE(t) */ * FROM table t WHERE a = 1 OR b = 2 - PostgreSQL 中更可靠的方式是写成
SELECT ... WHERE a = 1 UNION ALL SELECT ... WHERE b = 2,再配合SET enable_bitmapscan = off测试是否提升 - 检查执行计划里是否出现
type: index_merge(MySQL)或BitmapOr(PostgreSQL),而不是单纯的ALL或Seq Scan
复杂点在于:即使语法上只是几个 OR,一旦混入范围查询(>、BETWEEN)、LIKE 前缀模糊或函数调用,索引就大概率失效——这时候别硬扛,老老实实拆 UNION。










