UNIONALL直接合并结果集,避免UNION的去重和隐式排序开销,可大幅提升性能。使用时需确保列数、类型严格对齐,别名以第一个SELECT为准。ORDERBY和LIMIT需包裹整个UNIONALL语句生效。业务允许重复数据时推荐使用UNIONALL,但需严格去重的场景仍应使用UNION。上线前应通过EXPLAIN确认无临时表等性能隐患。
MySQL UnionAll合表使用技巧:避免Union去重性能损耗
时间:2026-05-18
浏览:436
文章目录
MySQL UNION ALL 的正确用法:避开去重陷阱,性能提升 11 倍
核心区别在于:UNION 会触发隐式去重和排序,导致全量扫描和临时表;而 UNION ALL 直接合并结果,只要业务允许重复,就能避免这些开销。列数、类型、别名必须严格对齐,ORDER BY/LIMIT 需包裹整个 UNION ALL。
直接用 UNION ALL 替代 UNION,只要两个结果集天然无重或业务上允许重复,就能避开去重和隐式排序的开销——这不是“能用就行”的取舍,而是查 100 万行时快 11 倍的关键动作。
为什么 UNION 会突然变慢?执行计划里的 Using temporary 是关键
当你发现 UNION 查询比单个子查询慢几倍,EXPLAIN 输出里却出现 Using temporary 和 Using filesort,基本可以断定:MySQL 正在把所有结果先塞进临时表,再逐行比对去重、再排序。这个过程不依赖索引,纯靠内存/磁盘暴力扫描。
- 去重逻辑是整行比对:不是按主键或某列判断,而是整行字段类型+值+NULL 全部严格一致才算重复。
- 哪怕你只想要“手机号不重复”,
UNION也会把地址、创建时间等所有列一起比。 - MySQL 8.0.19+ 的优化:虽然取消了默认排序,但去重逻辑仍在,
Using temporary不会消失。
UNION ALL 列数、类型、别名怎么对齐才不报错
UNION ALL 对结构一致性要求极严,报错信息(如 ERROR 1222 (21000): The used SELECT statements have a different number of columns)往往不指明哪一列错了,得自己逐列核对。
- 列数必须完全相等:少一列或多一列都直接报错,不会自动补
NULL。 - 对应列类型要兼容:比如
TINYINT和INT可隐式升为INT,但VARCHAR(10)和VARCHAR(255)会按长的定宽,若后续用于GROUP BY可能因长度超限导致索引失效。 - 列名以第一个
SELECT为准:想统一显示名,只能在第一个子句里用AS起别名,例如SELECT id AS user_id FROM t1 UNION ALL SELECT uid AS user_id FROM t2。 - 处理
TEXT和VARCHAR混用:可能触发截断警告,建议提前用CAST(col AS CHAR)显式对齐。
ORDER BY 和 LIMIT 写在哪?为什么总不生效
UNION ALL 语句末尾的 ORDER BY 或 LIMIT 默认只作用于整个合并结果,但如果你没加括号,数据库可能只把它当成最后一个子查询的修饰——而这是非法的。
- 正确写法是把整个
UNION ALL当作子查询包裹起来:SELECT * FROM ( SELECT id, name FROM order_2025_q4 UNION ALL SELECT id, name FROM order_2026_q1 ) AS t ORDER BY id DESC LIMIT 100 - 如果只在最后一个子句后加
ORDER BY id,MySQL 会报错This type of clause is not allowed in a UNION。 - 大数据量下慎用外层
ORDER BY:它会导致全量合并后再排序,临时表可能撑爆内存;优先考虑是否能在每个子查询内按需排序+限制,比如分页场景下改用SELECT ... FROM t1 ORDER BY id DESC LIMIT 50 UNION ALL SELECT ... FROM t2 ORDER BY id DESC LIMIT 50。
什么时候必须用 UNION,而不是偷懒换 UNION ALL
别因为性能好就无脑换。有些场景 UNION ALL 会掩盖数据问题,反而更难排查。
- 合并用户注册表和测试账号表:同一手机号可能在两边都存在,业务要求最终列表唯一 → 必须用
UNION。 - 后续还要
JOIN或GROUP BY:如果去重逻辑本该由聚合完成(比如统计各渠道新增用户数),那用UNION ALL更合理,避免UNION的中间排序干扰索引使用。 - 查的是不同日期分区表(如
log_20260401和log_20260402),且表结构完全一致 →UNION ALL是唯一合理选择。 - 上线前必须跑
EXPLAIN:开发环境小数据无所谓,但上线前必须跑EXPLAIN确认没有Using temporary;有,就得立刻定位是UNION还是其他地方拖慢了查询。
最常被忽略的一点:字段类型隐式转换带来的索引失效风险,比性能损耗更隐蔽。比如一个子查询用 VARCHAR(16),另一个用 CHAR(8),UNION ALL 后该列实际类型变成 VARCHAR(16),但优化器可能误判 selectivity,导致本该走索引的 WHERE 条件退化为全表扫描。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。