UNION ALL能替代OR,但仅适用于OR连接同一表上互斥且可独立走索引的条件(如status = 1 OR type = 'urgent'),若OR同字段(a = 1 OR a = 2)或含非索引条件(LIKE、函数等)则不应替换。

UNION能替代OR吗?先看执行计划
能,但不是所有OR都适合用UNION ALL替换。MySQL对OR的优化能力有限,尤其当OR两边条件涉及不同索引、或其中一边无法走索引时,type常退化为ALL或index——这时EXPLAIN里会明确显示Using where; Using filesort甚至Using temporary。
真正该换的场景是:OR连接的是**同一张表上互斥、可独立走索引的条件**,比如status = 1 OR type = 'urgent',且status和type各自有单列索引或组合索引前导列。
- 如果
OR两边共用同一个索引(如WHERE a = 1 OR a = 2),不用改,B+树范围扫描就能搞定 - 如果一边是
LIKE '%abc'或DATE(created_at) = '2025-01-01',对应字段索引基本失效,强行拆成UNION ALL也救不回来 - 跨表
OR(如t1.id = t2.ref_id OR t3.id = t2.ref_id)不适合用UNION ALL,应优先考虑重构JOIN逻辑
怎么把OR重写成UNION ALL?关键三步
重写不是简单切开OR,而是让每个分支变成**可独立命中索引的完整查询**。例如原SQL:
SELECT id, name, created_at FROM orders WHERE status = 1 OR type = 'refund';
正确重写方式:
(SELECT id, name, created_at FROM orders WHERE status = 1) UNION ALL (SELECT id, name, created_at FROM orders WHERE type = 'refund' AND status != 1);
注意第二分支加了AND status != 1——这是为了语义等价(避免重复行),但更推荐业务层接受重复(即去掉这个条件),因为UNION ALL本就不去重,而status = 1和type = 'refund'在业务上大概率无交集。
- 每个
SELECT必须包含完全相同的列数、顺序和兼容类型;少一列或类型不匹配直接报错 - 别漏掉
WHERE下推:原OR外的其他条件(如AND deleted = 0)要复制到每个子查询里 - 如果原查询有
ORDER BY ... LIMIT,必须拆到每个子查询内,例如ORDER BY created_at DESC LIMIT 20,否则外层排序会拖垮性能
为什么UNION ALL比OR快?索引怎么配
因为MySQL对OR的执行器处理很朴素:它不会为OR两侧分别走索引再合并,而是倾向选一个索引(甚至全表扫),再用Using where过滤另一侧条件。而UNION ALL强制拆成两个独立查询,每个都能走自己最合适的索引。
所以索引必须按分支单独建:
- 第一分支
WHERE status = 1→ 需要索引(status, id, name, created_at)(覆盖索引,避免回表) - 第二分支
WHERE type = 'refund'→ 需要索引(type, id, name, created_at) - 别指望一个
(status, type)联合索引能同时服务两个分支——OR条件下,它只可能走前导列status,type部分被忽略
如果表上有高频OR查询模式,这类索引成本是刚性支出,没得省。
容易踩的坑:临时表、重复数据、NULL陷阱
即使重写成功,仍可能慢——问题常出在中间结果太大,触发磁盘临时表。用SHOW STATUS LIKE 'Created_tmp%'确认是否出现Created_tmp_disk_tables > 0。
-
tmp_table_size和max_heap_table_size必须设够大(比如256M),否则UNION ALL合并后的结果哪怕只有几万行,也可能落盘 - 如果两个分支结果有真实交集(比如某行同时满足
status = 1和type = 'refund'),UNION ALL会返回两遍,业务代码得自己去重或接受冗余 -
OR对NULL的判断(如col IS NULL OR col = 5)不能直接拆——IS NULL需要特殊索引支持(如col字段允许NULL时,(col)索引对IS NULL有效,但(col, other)不一定)
最麻烦的是,这种优化往往只对特定查询生效,换一个OR条件就得重新分析索引和执行计划,没法一劳永逸。


















