SQL UNION 与 UNION ALL:差异、示例和性能测试

  1. 首页
  2. 博客
  3. 数据库和 SQL

数据库与 SQL

SQL UNION 与 UNION ALL:差异、示例和性能测试

它们之间的区别只有一个词,但选择错误可能会默默地给你错误的总数。查看两者的真实数据,了解它们共享的规则,并了解我们对它们进行一百万行计时时发生的情况。

阅读约 8 分钟
SQL UNION 与 UNION ALL:差异、示例和性能测试

要点速览

  • UNION合并结果并删除重复行。 UNION ALL 保留每一行。
  • 重复数据删除会产生成本,因此 UNION ALL 通常计算速度更快。
  • 在 SUM 之前使用 UNION 会默默地删除实际值并给出错误的答案。
  • 默认为 UNION ALL,除非您特别希望删除重复项。

两个运算符都将两个查询的结果堆叠在一起,因此 SQL UNION 与 UNION ALL 可能看起来是一个次要的选择。它不是。多一个词就会改变你的结果以及数据库的工作强度。

我们在 SQLite 3.50 中运行了每个查询,显示的结果和计时来自这些运​​行。基本行为是跨主要数据库的标准 SQL。然而,时间会因您自己的数据库、数据和硬件而异,因此请以我们的为例。

简短的回答

UNION 组合两个结果集并仅返回不同的行。 UNION ALL 将它们组合起来并返回所有内容,包括重复项。这就是全部区别,本文的其余部分将讨论其重要性的原因。

UNION 与 UNION ALL 并排

方面 联盟 联合所有
重复行 已删除 保留
额外的工作 必须找到重复项 无
常用速度 较慢 更快
返回的行数 相同或更少 他们全部
在 SUM 或 COUNT 之前安全吗? 通常不会 是的
最适合 独特的清单 合并数据、日志、总计

样本数据

想象一下一家商店将在线订单和店内订单保存在不同的表中。请注意,Asha 有两份相同的在线订单和一份相同的实体店订单。

CREATE TABLE online_orders (customer TEXT, total INT);
CREATE TABLE store_orders (customer TEXT, total INT);

INSERT INTO online_orders VALUES ('Asha',100), ('Ben',200), ('Asha',100);
INSERT INTO store_orders VALUES ('Asha',100), ('Dev',300);

查看差异

SELECT customer, total FROM online_orders
UNION
SELECT customer, total FROM store_orders;

-- Asha | 100
-- Ben  | 200
-- Dev  | 300
SELECT customer, total FROM online_orders
UNION ALL
SELECT customer, total FROM store_orders;

-- Asha | 100
-- Ben  | 200
-- Asha | 100
-- Asha | 100
-- Dev  | 300

五个输入行变为 UNION 的三行,所有五个输入行都保留为 UNION ALL。需要注意两点:

  • UNION从整个结果中删除了重复项,包括第一个表中的重复项。它不仅仅是跨表比较。
  • 仅当每个选定列 匹配时,行才算作重复行。

当您确实想要一个独特的列表时,UNION 是完全正确的。这为通过任一渠道购买的每位客户提供:

SELECT customer FROM online_orders
UNION
SELECT customer FROM store_orders;

-- Asha
-- Ben
-- Dev

错误总数错误

这就是选择真正重要的原因。假设您想要总收入。上面的订单加起来是800。看看每个操作员给出的是什么:

SELECT SUM(total) FROM (
  SELECT total FROM online_orders
  UNION
  SELECT total FROM store_orders
);
-- 600

SELECT SUM(total) FROM (
  SELECT total FROM online_orders
  UNION ALL
  SELECT total FROM store_orders
);
-- 800

UNION 版本错误了 200。因为我们只选择了 total 列,所以三个单独的 100 卢比订单看起来像是重复的,并合并为一个。没有错误消息,600 看起来是一个完全可信的数字。这种悄无声息的错误可能会在财务报告中停留数月之久。

提示: 两个看起来相同的行可能是两个真实事件。在使用 UNION 之前,询问相同的行是否意味着记录了两次的相同事实,或者两个不同的事实。对于销售、付款和日志条目,通常是第二个。

双方都必须遵守的规则

两个运营商都有相同的要求:

  1. 每个 SELECT 必须返回 相同数量的列。
  2. 列按 位置 匹配,而不是按名称匹配,因此请保持它们的顺序相同。
  3. 每个位置的数据类型必须兼容。
  4. 结果列名称来自 first SELECT。
  5. ORDER BY位于最后,它适用于整个结果。

违反第一条规则会产生错误。这是来自 SQLite 的消息:

SELECT customer, total FROM online_orders
UNION
SELECT customer FROM store_orders;

-- SELECTs to the left and right of UNION do not have
-- the same number of result columns

第二条规则更危险,因为它可能会在没有错误的情况下失败。如果交换相同类型的两列,例如 first_name 和 last_name,查询将运行并且数据会出现错误。 SQLite 对类型也很宽松,而 PostgreSQL 等数据库则更严格,可能需要显式 CAST。

标记来源

一个非常实用的技巧是添加一个常量列来显示每行的来源。由于标签不同,来自不同表的行不能再被视为彼此的重复项。

SELECT customer, total, 'online' AS channel FROM online_orders
UNION ALL
SELECT customer, total, 'store' FROM store_orders
ORDER BY customer, channel;

-- Asha | 100 | online
-- Asha | 100 | online
-- Asha | 100 | store
-- Ben  | 200 | online
-- Dev  | 300 | store

一旦您的数据有了标签,您就可以使用 GROUP BY 将其分组以按渠道报告销售情况。如果您需要组合多个步骤,请将联合包装在 CTE 中以保持可读性。

性能:我们的测试中发生了什么

要删除重复项,数据库必须比较行,通常通过对行进行排序或散列来比较。 UNION ALL可以跳过此步骤。我们创建了两个表,每个表有 500,000 行,包含从 0 到 50,000 的随机数,因此存在大量重复项。我们将每个查询运行多次并保留最佳时间。

首先,我们计算结果,以便将行发送回我们的程序所花费的时间不会影响比较:

查询,包含在 COUNT(*) 中 行数 最佳时间
联盟 50,001 261 毫秒
联合所有 1,000,000 1 毫秒以下

巨大差距的部分原因是 SQLite 可以在不读取每一行的情况下计算出该计数,因此请不要将其理解为“快数千倍”。不过,它确实展示了核心思想:UNION 必须做实际工作,而 UNION ALL 没有。

然后我们将所有行提取到Python中,结果发生了翻转:

查询,获取所有行 返回的行数 最佳时间
联盟 50,001 277 毫秒
联合所有 1,000,000 392 毫秒

在这里,UNION ALL 花费了更长的时间,因为它交付的行数是原来的 20 倍。重复数据删除并不是减慢速度的原因。处理一百万行是。因此,诚实的规则是,UNION ALL 对于数据库的计算来说更便宜,但结果的大小也很重要。许多在线图表将其简化为“UNION ALL 总是更快”,而我们的测试表明了为什么它没那么简单。

也不要仅根据速度进行选择。首先选择给出正确答案的操作员,然后进行调整。如果查询速度很慢,请阅读我们的 SQL 查询优化指南。

何时使用每个

在以下情况下使用 UNION ALL:

  • 您正在合并来自类似表的数据,例如每月分区。
  • 之后您将计算总数或计数。
  • 您知道输入不能重叠。
  • 重复是有意义的,就像日志或审计记录一样。

在以下情况下使用 UNION:

  • 您需要一个唯一的列表,例如两个系统中的每个电子邮件地址。
  • 重叠的来源多次包含相同的事实。

将UNION ALL视为默认值,并有意使用UNION。向每个分支添加一个 WHERE 子句以尽早进行过滤,这对两者都有帮助。

相交和除外

两个相关的集合运算符回答不同的问题。两者都遵循相同的列规则:

SELECT customer FROM online_orders
INTERSECT
SELECT customer FROM store_orders;
-- Asha  (bought in both channels)

SELECT customer FROM online_orders
EXCEPT
SELECT customer FROM store_orders;
-- Ben   (online only)

Oracle 传统上使用 MINUS 而不是 EXCEPT,并且对这些运算符的支持因数据库和版本而异。

联合与连接

这些经常被混淆,但它们结合了不同方向的数据:

操作 方向 结果
联盟 垂直 添加更多行
加入 卧式 添加更多列

常见错误

  • 习惯使用UNION。这可能会降低你的速度,或者删除有效的行。
  • 选择的列太少。 省略 ID 会使不同的行看起来相同。
  • 列顺序不匹配。 值可能会出现在错误的列中,但不会出现任何错误。
  • 为每个分支添加 ORDER BY。 在最后排序一次。
  • 使用 UNION 修补错误的连接。 如果由于连接而出现重复,请修复连接。
  • 假设结果顺序。 如果没有 ORDER BY,则不能保证任一运算符的行顺序。

最后的话

每次问一个问题:相同的行是相同的事实,还是不同的事实?如果它们是分开的,或者不可能重叠,请使用 UNION ALL。如果您需要唯一的列表,请使用 UNION。要检查您自己的报告,请在代码中搜索 SUM 或 COUNT 附近的 UNION,并确认这些总计确实正确。

常见问题

UNION 和 UNION ALL 有什么区别?

UNION 组合两个结果集并删除重复的行。 UNION ALL 将它们组合起来并保留每一行,包括重复的行。

UNION 和 UNION ALL 哪个更快?

UNION ALL 的计算成本通常较低,因为它不检查重复项。正如我们的测试所示,返回更大的结果总体上仍然需要更长的时间。

UNION 是否会删除一张表中的重复项?

是的。它删除整个组合结果中的重复项,包括全部来自同一个表的相同行。

UNION 中的列名必须匹配吗?

不会。列按位置匹配,结果使用第一个 SELECT 中的名称。列数必须匹配,并且类型必须兼容。

我可以将 ORDER BY 与 UNION 一起使用吗?

是的,但是在语句的最后放置一个 ORDER BY。它对整个组合结果而不是单个分支进行排序。

继续学习

与Stack Quora合作

需要测试、WordPress 或 SEO 方面的帮助?

我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。

继续阅读

更多指南 测试、人工智能和现代开发

喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。

人工智能

人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它

人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。

阅读文章 →
DevOps

环境变量解释:.env 文件、秘密和配置正确完成

使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。

阅读文章 →
软件开发

API 速率限制解释:算法、标头和处理 429 错误

API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。

阅读文章 →
测试自动化

Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误

使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。

阅读文章 →
软件测试

代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义

用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。

阅读文章 →
数据库与 SQL

SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。

阅读文章 →
数据库与 SQL

SQL 视图解释:创建、使用和避免陷阱(附示例)

SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。

阅读文章 →
软件开发

TypeScript 接口与类型:真正的差异,使用编译器进行测试

TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。

阅读文章 →
软件开发

Python *args 和 **kwargs 解释:灵活的函数和清晰的示例

Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。

阅读文章 →
软件开发

Python Lambda 函数:语法、实际用途以及何时不使用它们

Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。

阅读文章 →
人工智能

什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环

什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。

阅读文章 →
DevOps

监控与可观察性:真正的区别,包括日志、指标和跟踪

监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。

阅读文章 →
DevOps

Docker Volumes 与 Bind Mounts:以正确的方式保留容器数据

Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。

阅读文章 →
测试自动化

TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式

TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。

阅读文章 →
软件测试

用户验收测试 (UAT):流程、清单和真实示例

用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。

阅读文章 →
数据库与 SQL

SQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱

通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。

阅读文章 →
数据库与 SQL

SQL CASE WHEN 语句:语法、实例和常见错误

SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。

阅读文章 →
软件开发

什么是智威汤逊?通过工作示例解释 JSON Web 令牌

什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。

阅读文章 →
软件开发

JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例

JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。

阅读文章 →
软件开发

Python 生成器和 Yield 解释:通过真实示例节省内存

Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。

阅读文章 →
人工智能

什么是嵌入?向量和余弦相似度简单解释

什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。

阅读文章 →
DevOps

蓝绿部署与金丝雀部署:差异、权衡以及如何选择

蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。

阅读文章 →
测试自动化

不稳定的测试:造成这些问题的原因以及如何彻底解决它们

我们运行的演示解释了不稳定的测试:主要原因、如何确认和诊断、正确的时间修复、共享状态和日期以及隔离过程。

阅读文章 →
软件测试

负载测试与压力测试:差异、指标和 k6 示例

负载测试与压力测试的解释:各自证明什么、峰值和浸泡测试、重要的指标、为什么平均值会误导、k6 个示例以及如何计划测试。

阅读文章 →
软件测试

测试用例与测试场景:主要区别、示例以及何时使用它们

测试用例与测试场景的解释:清晰的定义、并排的表格、从场景到案例的密码重置功能、模板以及何时使用每个功能。

阅读文章 →
数据库与 SQL

SQL 查询优化:读取执行计划并修复慢速查询(带计时)

对一百万行进行 SQL 查询优化测试:读取执行计划、添加正确的索引、修复不可控制的过滤器、使用复合索引和键集分页。

阅读文章 →
数据库与 SQL

SQL UNION 与 UNION ALL:差异、示例和性能测试

SQL UNION 与 UNION ALL 通过我们运行的查询进行了解释:重复、都遵循的规则、真实的数据错误、测量的性能、INTERSECT 和 EXCEPT 以及何时使用它们。

阅读文章 →
软件开发

JavaScript Fetch API 教程:GET、POST、错误处理和超时

针对本地服务器测试的 JavaScript Fetch API 教程:GET 和 POST、为什么不抛出 404、可重用的帮助程序、查询字符串、超时、CORS 和常见错误。

阅读文章 →
软件开发

Python 文件处理:读取、写入、CSV、JSON 和 pathlib 以及示例

使用我们运行的代码解释了 Python 文件处理:打开模式、使用、读取大文件、CSV、JSON、pathlib、二进制文件、安全写入、错误处理和常见错误。

阅读文章 →
软件开发

Python 类和对象:带有实际示例的 OOP 教程

用我们运行的代码解释 Python 类和对象:__init__、方法、类与实例属性、继承、属性、dunder 方法、数据类和常见错误。

阅读文章 →
人工智能

什么是大语言模型?法学硕士如何运作,在没有炒作的情况下进行解释

什么是大语言模型?法学硕士用简单的语言解释:标记、嵌入、注意力、训练、温度和上下文窗口、一个微小的下一个单词演示和真正的限制。

阅读文章 →
DevOps

适合初学者的 Linux 命令:带有示例的基本终端指南

适合初学者的 Linux 命令,按任务分组:导航、文件、查看日志、grep、管道、权限、进程和档案,并附有实用的日志分析示例。

阅读文章 →
测试自动化

Selenium 与 Playwright:主要区别、代码比较以及选择

Selenium 与 Playwright 比较:架构、等待、浏览器和语言支持、调试、两种工具中的相同测试、迁移建议以及如何选择。

阅读文章 →
软件开发

Git merge 与 rebase 的区别:示例及如何选择

用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。

阅读文章 →
软件开发

Python 列表推导式:语法、示例与何时避免使用

Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。

阅读文章 →
数据库与 SQL

SQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)

通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。

阅读文章 →