SQL UNION 组合查询详解
您问到了一个非常关键的问题!让我详细讲解 UNION 组合查询的规则和用法。
一、直接回答:列名不必完全相同,但列的数量和数据类型必须匹配
sql
-- ✅ 正确:列名不同,但数量和类型匹配SELECT emp_id AS 编号, emp_name AS 姓名 FROM empUNIONSELECT dept_id, dept_name FROM dept;-- ❌ 错误:列数不同SELECT emp_id, emp_name FROM empUNIONSELECT dept_id FROM dept; -- 第一句2列,第二句1列-- ❌ 错误:数据类型不匹配SELECT emp_id, emp_name FROM empUNIONSELECT dept_id, 100 FROM dept; -- 第二列:字符串 vs 数字
关键规则:
| 必须匹配 | 不必相同 |
|---|---|
| 列的数量 | 列的名称 |
| 列的数据类型 | 列的别名 |
| 列的顺序 | 列的来源表 |
二、UNION 的词源
| 单词 | 词源 | 含义 |
|---|---|---|
| UNION | 拉丁语 unio(合一、联合) | 联合、合并、并集 |
词源演变
text
拉丁语 unus(一) ↓ 拉丁语 unio(合成一体、联合) ↓ 古法语 union(联合) ↓ 英语 union(联合、合并)
同源词
| 单词 | 含义 | 关系 |
|---|---|---|
| unite | 联合、统一 | 使成为一体 |
| unit | 单元、单位 | 一个整体 |
| unique | 独特的 | 唯一的 |
| unify | 统一 | 使成为一体 |
三、UNION 的三种类型
类型 | 关键词 | 行为 | 重复行 |
UNION | UNION | 合并 + 去重 | 自动去除重复 |
UNION ALL | UNION ALL | 合并 + 保留所有 | 保留重复 |
UNION DISTINCT | UNION DISTINCT | 合并 + 去重(同 UNION) | 自动去除重复 |
四、UNION 的工作原理解析
字面含义
UNION = 把两个结果集"联合"在一起,像一个纵向拼接的过程。
可视化理解
text
SELECT 结果集 A: SELECT 结果集 B: ┌───────┬───────┐ ┌───────┬───────┐ │ 编号 │ 姓名 │ │ 编号 │ 姓名 │ ├───────┼───────┤ ├───────┼───────┤ │ 101 │ 张三 │ │ 103 │ 王五 │ │ 102 │ 李四 │ │ 104 │ 赵六 │ └───────┴───────┘ └───────┴───────┘ UNION ↓ ┌─────────────────────────────────────────────┐ │ UNION 结果: │ │ ┌───────┬───────┐ │ │ │ 编号 │ 姓名 │ │ │ ├───────┼───────┤ │ │ │ 101 │ 张三 │ ← 来自 A │ │ │ 102 │ 李四 │ ← 来自 A │ │ │ 103 │ 王五 │ ← 来自 B │ │ │ 104 │ 赵六 │ ← 来自 B │ │ └───────┴───────┘ │ └─────────────────────────────────────────────┘
五、UNION vs UNION ALL 的区别
示例数据
sql
-- 表A:2023年订单SELECT order_id FROM orders_2023; -- 101, 102, 103-- 表B:2024年订单SELECT order_id FROM orders_2024; -- 103, 104, 105
结果对比
| 写法 | 结果 | 说明 |
|---|---|---|
UNION | 101, 102, 103, 104, 105 | 去重(103出现一次) |
UNION ALL | 101, 102, 103, 103, 104, 105 | 保留重复(103出现两次) |
六、UNION 的完整语法
sql
SELECT 列1, 列2, ...FROM 表1[WHERE 条件]UNION [ALL | DISTINCT]SELECT 列1, 列2, ...FROM 表2[WHERE 条件][ORDER BY 列]
关键规则
| 规则 | 说明 | 示例 |
|---|---|---|
| 列数相同 | 两个 SELECT 的列数必须相等 | 都是3列 |
| 数据类型兼容 | 对应列的数据类型必须兼容 | 数字对数字,字符对字符 |
| 列名取第一个 | 结果集的列名来自第一个 SELECT | 第一句的别名生效 |
| ORDER BY 放最后 | 排序只能在整个 UNION 末尾 | 不能分别排序 |
| 自动去重 | UNION 会去除重复行 | UNION ALL 保留重复 |
七、实际应用场景
场景1:合并不同年份的数据
sql
-- 合并2023年和2024年的订单SELECT order_id, order_date, '2023' AS 年份 FROM orders_2023UNION ALLSELECT order_id, order_date, '2024' FROM orders_2024ORDER BY order_date;
场景2:合并不同表的结构化数据
sql
-- 合并员工和客户为"联系人"列表SELECT emp_name AS 姓名, emp_phone AS 电话, '员工' AS 类型 FROM empUNIONSELECT cust_name, cust_phone, '客户' FROM customer;
场景3:数据去重
sql
-- 找出所有有产品的品牌(去重)SELECT brand_id FROM products_2023UNIONSELECT brand_id FROM products_2024;-- 等价于SELECT DISTINCT brand_id FROM ( SELECT brand_id FROM products_2023 UNION ALL SELECT brand_id FROM products_2024) t;
八、列名不同但能工作的示例
sql
-- 列名完全不同,但能正常执行SELECT emp_id AS 员工编号, emp_name AS 员工姓名 FROM empUNIONSELECT dept_id, dept_name FROM dept;
结果集:
| 员工编号 | 员工姓名 | |
|---|---|---|
| 101 | 张三 | |
| 102 | 李四 | |
| 10 | 财务部 | ← 来自 dept 表的 dept_id, dept_name |
九、常见错误及解决
| 错误 | 原因 | 解决方案 |
|---|---|---|
ORA-01789 | 列数不同 | 确保两个 SELECT 列数相同 |
ORA-01790 | 数据类型不匹配 | 转换数据类型 |
| 无错误但结果不对 | 列顺序不对 | 调整列的顺序 |
| ORDER BY 报错 | ORDER BY 位置错误 | 移到 UNION 末尾 |
数据类型转换示例
sql
-- 错误:数据类型不匹配SELECT emp_id, emp_name FROM empUNIONSELECT dept_id, 100 FROM dept; -- 100是数字,emp_name是字符串-- 正确:显式转换SELECT emp_id, emp_name FROM empUNIONSELECT dept_id, TO_CHAR(100) FROM dept;
十、总结
| 问题 | 答案 |
|---|---|
| 列名必须相同吗? | ❌ 不必,但列数和数据类型必须匹配 |
| 结果集的列名从哪来? | 来自第一个 SELECT 语句 |
| UNION 和 UNION ALL 区别? | UNION 去重,UNION ALL 保留重复 |
| 词源 | 拉丁语 unio(合一、联合) |
| 主要用途 | 合并多个查询结果、汇总不同来源数据 |
| 性能建议 | 不需要去重时用 UNION ALL(更快) |
一句话总结:UNION 源自古拉丁语"合一、联合",在 SQL 中用于纵向合并多个查询结果。列名不必相同,但列数和数据类型必须匹配;UNION 自动去重,UNION ALL 保留所有行。就像把两张表格上下"粘"在一起——列的数量和类型要对齐,但列的名字可以不同