SQL视图UNION ALL效率高于UNION的原因

作者:技术拾光时间:2026-06-17 08:43:07浏览:306

先说几个核心判断:UNION ALL 比 UNION 快,根本原因不是“少做了一个去重操作”,而是它直接跳过了整个去重+排序的数据处理链路——数据库压根不会启动那套机制。换句话说,一个是走完全流程,一个是抄近道直达,结果自然是天壤之别。 100 万行级别的数据量,慢 11 倍只是起步价。如果嵌套在视

先说几个核心判断:UNION ALL 比 UNION 快,根本原因不是“少做了一个去重操作”,而是它直接跳过了整个去重+排序的数据处理链路——数据库压根不会启动那套机制。换句话说,一个是走完全流程,一个是抄近道直达,结果自然是天壤之别。

100 万行级别的数据量,慢 11 倍只是起步价。如果嵌套在视图里再套一层 LIMIT OFFSET,这个代价翻倍都不止。更关键的是,很多人并不清楚这种性能衰减到底从何而来。

UNION 实际触发的是 DISTINCT + ORDER BY 两层开销

  • UNION 不是简单的“先合并再筛选”,而是后台悄悄搞了一套集合归并流程:所有子查询的结果必须先写入临时结构(内存或者磁盘),然后做哈希或排序去重,最后再默认按第一列升序输出——即便你没写 ORDER BY,它也会这么干。
  • 看执行计划你会发现,Using temporary 和 Using filesort 是常客。尤其当参与列没有索引、数据量超过 5 万行时,I/O 和 CPU 的压力会迅速成为瓶颈。
  • 更有意思的是,就算你在外层加了 ORDER BY id,数据库也有可能执行两次排序:一次是为了去重,一次是为了你显式指定的顺序。这个“隐性双倍开销”,才是容易被忽视的坑。

UNION ALL 的玩法完全不同——纯流式拼接,零中间态

  • 它不做任何行级比较,不申请额外内存建哈希表,不写临时文件,也不调整顺序。第一个 SELECT 开始返回数据,第二个紧接着追加,全程保持 pipeline 模式。从执行计划的 EXPLAIN 输出就能一眼看出来:干干净净,没有任何 Using temporary 或 Using filesort。
  • 对于分表查询(比如 log_202604 和 log_202605)、状态互斥查询(比如 status = 1 和 status = 2),UNION ALL 不仅在语义上更准确,性能表现也更可预期——不会因为数据量的波动而突然变慢。

别被“宽容”的假象迷惑——列对齐和类型兼容性要求完全一致

  • UNION ALL 并不比 UNION 更“好说话”:列数不等、类型不兼容(例如 INT 和 JSON)、NULL 性约束冲突(NOT NULL 与 NULLable),它一样会报错。别指望用 UNION ALL 来绕过类型检查。
  • 一个典型的例子:MySQL 8.0+ 在严格模式下,如果第一条 SELECT 返回的是 NOT NULL VARCHAR(50),第二条返回 NULL,它直接拒绝执行,不给任何通融余地。
  • 列名永远以第一个 SELECT 为准——后续子查询里的别名(比如 SELECT id AS uid)是无效的。这个规则在很多真实场景中引发过令人困惑的 bug。

外层 ORDER BY 才是唯一可控的排序方式

  • 子查询里写 ORDER BY 基本是白费功夫——语法允许,但实际被忽略。UNION 的隐式排序规则不可靠(可能按主键,也可能按表达式推导出的列),UNION ALL 更是完全依赖执行顺序。
  • 正确的写法只有一种:
SELECT id, name FROM users_2023
UNION ALL
SELECT id, name FROM users_2024
ORDER BY id;
  • 少了这句 ORDER BY,结果顺序在不同 MySQL 版本、不同执行路径下都可能变化。上线后一旦前端渲染错乱或者分页错位,排查起来相当费力——因为问题不是每次都出现,而是随机性的。

说到底,UNION 的性能衰减是非线性的。100 万行时慢 11 倍,嵌套在视图里再加一层 LIMIT OFFSET,代价可能翻倍还不止。真正值得关注的不是“多用 UNION ALL 还是 UNION”这个选择题,而是理解数据库底层到底做了什么、跳过了什么、以及这些差异在日常开发中会怎样累积成性能的压倒性差距。