MySQL UNION 操作符(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在数据库操作中,经常需要将两个或多个查询结果合并为一个统一的数据集。此时,MySQL UNION 操作符 就像一把灵活的瑞士军刀,能够帮助开发者高效整合数据。无论是报表生成、多表聚合还是复杂查询场景,UNION 都是提升开发效率的核心工具之一。对于编程初学者,理解其基础语法和逻辑是关键;而对中级开发者,掌握其性能优化和进阶用法则能进一步提升技术深度。


一、基础语法与核心逻辑

1.1 基本语法结构

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。其核心语法如下:

SELECT column1, column2, ...  
FROM table1  
UNION  
SELECT column1, column2, ...  
FROM table2;  

关键规则

  • 所有 SELECT 语句的列数必须一致。
  • 对应列的 数据类型 必须兼容(如整数与浮点数可合并,但字符串与数值类型不可合并)。
  • 列名以第一个 SELECT 的列名为准。

形象比喻:可以将 UNION 视为“合并两个文件夹”的操作。假设两个文件夹分别存放了不同部门的员工信息,UNION 可以将它们合并为一个总文件夹,但会自动删除重复的文件(即去重)。


1.2 UNION 与 UNION ALL 的区别

UNION 默认会 自动去重,而 UNION ALL 则保留所有结果,包括重复行。

示例对比

假设表 employeescontractors 各有以下数据:
| employees | contractors |
|------------------|--------------------|
| Alice (ID: 1) | Bob (ID: 2) |
| Bob (ID: 2) | Charlie (ID: 3) |

执行以下查询:

SELECT name FROM employees  
UNION  
SELECT name FROM contractors;  

结果会是:
| name |
|--------|
| Alice |
| Bob |
| Charlie|

而使用 UNION ALL

SELECT name FROM employees  
UNION ALL  
SELECT name FROM contractors;  

结果会包含重复的 Bob
| name |
|--------|
| Alice |
| Bob |
| Charlie|
| Bob |

性能提示:如果明确知道数据无重复或无需去重,建议使用 UNION ALL,因为去重操作会消耗额外的计算资源。


二、使用场景与实际案例

2.1 合并不同表的同类数据

场景:公司有两个部门表,需要统计所有员工的姓名和邮箱。

表结构

  • department_a 包含 nameemail 列。
  • department_b 包含 full_name(即姓名)和 contact_email(即邮箱)列。

解决方案

SELECT name, email  
FROM department_a  
UNION  
SELECT full_name, contact_email  
FROM department_b;  

注意:虽然列名不同,但只要数据类型一致,UNION 仍可合并。结果列名以第一个 SELECT 的列名为准(即 nameemail)。


2.2 生成动态筛选条件的聚合结果

场景:需要根据用户输入动态拼接查询条件,例如从不同表中筛选出特定地区的用户和商家。

表结构

  • users 表有 id, name, location 列。
  • merchants 表有 id, business_name, region 列。

查询示例

SELECT name AS entity_name, location AS area  
FROM users  
WHERE location = 'Shanghai'  
UNION  
SELECT business_name AS entity_name, region AS area  
FROM merchants  
WHERE region = 'Shanghai';  

结果将合并两类实体的上海地区信息,便于统一展示。


2.3 处理跨数据库或跨环境的查询

在某些架构中,数据可能分散在不同数据库或实例中。UNION 可以通过跨连接查询合并结果,例如:

SELECT * FROM database1.table1  
UNION  
SELECT * FROM database2.table2;  

但需确保两表结构完全一致。


三、进阶用法与注意事项

3.1 列数与类型的严格匹配

SELECT 语句的列数或类型不一致,MySQL 会报错。例如:

SELECT id, name FROM employees  -- 两列  
UNION  
SELECT department FROM departments;  -- 一列  

此查询会因列数不匹配而失败。

3.2 排序与分页的正确位置

ORDER BYLIMIT 必须 仅出现在最后一个 SELECT 语句之后,否则会因语法错误而失败。例如:

SELECT name FROM employees  
UNION  
SELECT name FROM contractors  
ORDER BY name ASC  -- 正确位置  
LIMIT 10;  

如果将 ORDER BY 放在第一个 SELECT 后面,则会报错。


3.3 性能优化技巧

  • 避免不必要的去重:若数据无重复或无需去重,改用 UNION ALL
  • 索引优化:对参与合并的列(如 nameid)建立索引,加速查询。
  • 减少列的数量:仅选择必要的列,避免全表扫描。

四、常见问题与解决方案

4.1 问题:合并结果中出现乱序

原因UNION 默认不保证结果顺序,除非显式使用 ORDER BY
解决方案:在最后一个 SELECT 后添加 ORDER BY 子句。

4.2 问题:数据类型不兼容导致错误

示例错误

ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'Alice'  

原因:合并的列中,一个表的列是 VARCHAR,另一个是 FLOAT
解决方案:统一列类型,例如将字符串转换为数值或反之。


五、实战案例:电商订单合并查询

5.1 场景描述

某电商平台有两个订单表:

  • orders:普通用户的订单(order_id, user_id, amount)。
  • wholesale_orders:批发商的订单(order_code, business_id, total)。

需求:统计所有订单的总金额,并按用户/批发商分类。

5.2 解决方案

SELECT  
  CONCAT('User_', user_id) AS entity_id,  
  amount AS total_amount  
FROM orders  
UNION ALL  
SELECT  
  CONCAT('Business_', business_id) AS entity_id,  
  total AS total_amount  
FROM wholesale_orders;  

此查询将两类订单合并,通过 CONCAT 统一标识符格式,并保留原始金额字段。


六、总结与扩展

通过本文,我们系统梳理了 MySQL UNION 操作符 的核心语法、使用场景及优化技巧。开发者需注意以下关键点:

  1. 基础规则:列数、类型匹配,去重逻辑。
  2. 性能优化:优先使用 UNION ALL,合理利用索引。
  3. 复杂场景:通过列别名、函数灵活处理异构数据。

对于进阶学习者,可进一步探索 UNION 与子查询、事务的结合,或结合 CASE 语句实现更复杂的条件合并。实践是掌握该操作符的最佳途径,建议读者通过实际项目或沙箱环境进行验证。


希望本文能成为您数据库技能提升的阶梯,祝您的开发旅程顺利!

最新发布