532 lines
11 KiB
Markdown
532 lines
11 KiB
Markdown
# MySQL UNION 语法使用文档
|
||
|
||
## 目录
|
||
1. [UNION 基础语法](#union-基础语法)
|
||
2. [UNION 规则和限制](#union-规则和限制)
|
||
3. [示例数据准备](#示例数据准备)
|
||
4. [UNION 基础使用示例](#union-基础使用示例)
|
||
5. [UNION vs UNION ALL](#union-vs-union-all)
|
||
6. [复杂查询示例](#复杂查询示例)
|
||
7. [最佳实践和注意事项](#最佳实践和注意事项)
|
||
|
||
## UNION 基础语法
|
||
|
||
UNION 用于合并两个或多个 SELECT 语句的结果集。
|
||
|
||
```sql
|
||
SELECT column1, column2, ... FROM table1
|
||
UNION [ALL]
|
||
SELECT column1, column2, ... FROM table2
|
||
[UNION [ALL]
|
||
SELECT column1, column2, ... FROM table3]
|
||
...
|
||
[ORDER BY column_name]
|
||
```
|
||
|
||
## UNION 规则和限制
|
||
|
||
1. **列数相同**:每个 SELECT 语句必须拥有相同数量的列
|
||
2. **数据类型兼容**:对应位置的列必须具有兼容的数据类型
|
||
3. **列名**:结果集使用第一个 SELECT 语句的列名
|
||
4. **去重**:UNION 默认去除重复记录,UNION ALL 保留所有记录
|
||
5. **ORDER BY**:只能在最后使用,对整个结果集排序
|
||
|
||
## 示例数据准备
|
||
|
||
### 创建示例表
|
||
|
||
```sql
|
||
-- 创建员工表
|
||
CREATE TABLE employees (
|
||
id INT PRIMARY KEY,
|
||
name VARCHAR(50),
|
||
department VARCHAR(30),
|
||
salary DECIMAL(10,2),
|
||
city VARCHAR(30)
|
||
);
|
||
|
||
-- 创建前员工表
|
||
CREATE TABLE former_employees (
|
||
id INT PRIMARY KEY,
|
||
name VARCHAR(50),
|
||
department VARCHAR(30),
|
||
last_salary DECIMAL(10,2),
|
||
city VARCHAR(30),
|
||
leave_date DATE
|
||
);
|
||
|
||
-- 创建管理层表
|
||
CREATE TABLE managers (
|
||
id INT PRIMARY KEY,
|
||
name VARCHAR(50),
|
||
level VARCHAR(20),
|
||
salary DECIMAL(10,2),
|
||
region VARCHAR(30)
|
||
);
|
||
```
|
||
|
||
### 插入示例数据
|
||
|
||
```sql
|
||
-- 插入员工数据
|
||
INSERT INTO employees (id, name, department, salary, city) VALUES
|
||
(1, '张三', '技术部', 8000.00, '北京'),
|
||
(2, '李四', '销售部', 6000.00, '上海'),
|
||
(3, '王五', '技术部', 9000.00, '深圳'),
|
||
(4, '赵六', '人事部', 5500.00, '北京'),
|
||
(5, '钱七', '财务部', 7000.00, '广州'),
|
||
(6, '孙八', '技术部', 8500.00, '杭州'),
|
||
(7, '周九', '市场部', 6500.00, '成都');
|
||
|
||
-- 插入前员工数据
|
||
INSERT INTO former_employees (id, name, department, last_salary, city, leave_date) VALUES
|
||
(8, '吴十', '技术部', 7500.00, '北京', '2023-08-15'),
|
||
(9, '郑一', '销售部', 5800.00, '上海', '2023-09-20'),
|
||
(10, '王五', '技术部', 8000.00, '深圳', '2023-07-10'),
|
||
(11, '刘二', '人事部', 5200.00, '广州', '2023-10-05');
|
||
|
||
-- 插入管理层数据
|
||
INSERT INTO managers (id, name, level, salary, region) VALUES
|
||
(12, '陈总', '高级经理', 15000.00, '华北'),
|
||
(13, '林总', '部门经理', 12000.00, '华东'),
|
||
(14, '张三', '项目经理', 10000.00, '华南'),
|
||
(15, '黄总', '区域经理', 11000.00, '华西');
|
||
```
|
||
|
||
## UNION 基础使用示例
|
||
|
||
### 1. 简单合并查询
|
||
|
||
合并当前员工和前员工的姓名:
|
||
|
||
```sql
|
||
SELECT name FROM employees
|
||
UNION
|
||
SELECT name FROM former_employees;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------+
|
||
| name |
|
||
+------+
|
||
| 张三 |
|
||
| 李四 |
|
||
| 王五 |
|
||
| 赵六 |
|
||
| 钱七 |
|
||
| 孙八 |
|
||
| 周九 |
|
||
| 吴十 |
|
||
| 郑一 |
|
||
| 刘二 |
|
||
+------+
|
||
```
|
||
|
||
### 2. 多列合并查询
|
||
|
||
合并员工和管理层的姓名和薪资:
|
||
|
||
```sql
|
||
SELECT name, salary FROM employees
|
||
UNION
|
||
SELECT name, salary FROM managers
|
||
ORDER BY salary DESC;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------+----------+
|
||
| name | salary |
|
||
+------+----------+
|
||
| 陈总 | 15000.00 |
|
||
| 林总 | 12000.00 |
|
||
| 黄总 | 11000.00 |
|
||
| 张三 | 10000.00 |
|
||
| 王五 | 9000.00 |
|
||
| 孙八 | 8500.00 |
|
||
| 张三 | 8000.00 |
|
||
| 钱七 | 7000.00 |
|
||
| 周九 | 6500.00 |
|
||
| 李四 | 6000.00 |
|
||
| 赵六 | 5500.00 |
|
||
+------+----------+
|
||
```
|
||
|
||
### 3. 使用别名统一列名
|
||
|
||
```sql
|
||
SELECT name, department AS work_area, salary FROM employees
|
||
UNION
|
||
SELECT name, region AS work_area, salary FROM managers
|
||
ORDER BY work_area;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------+-----------+----------+
|
||
| name | work_area | salary |
|
||
+------+-----------+----------+
|
||
| 钱七 | 财务部 | 7000.00 |
|
||
| 陈总 | 华北 | 15000.00 |
|
||
| 林总 | 华东 | 12000.00 |
|
||
| 张三 | 华南 | 10000.00 |
|
||
| 黄总 | 华西 | 11000.00 |
|
||
| 赵六 | 人事部 | 5500.00 |
|
||
| 李四 | 销售部 | 6000.00 |
|
||
| 周九 | 市场部 | 6500.00 |
|
||
| 张三 | 技术部 | 8000.00 |
|
||
| 王五 | 技术部 | 9000.00 |
|
||
| 孙八 | 技术部 | 8500.00 |
|
||
+------+-----------+----------+
|
||
```
|
||
|
||
## UNION vs UNION ALL
|
||
|
||
### UNION(去重)
|
||
|
||
```sql
|
||
SELECT city FROM employees
|
||
UNION
|
||
SELECT city FROM former_employees;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------+
|
||
| city |
|
||
+------+
|
||
| 北京 |
|
||
| 上海 |
|
||
| 深圳 |
|
||
| 广州 |
|
||
| 杭州 |
|
||
| 成都 |
|
||
+------+
|
||
```
|
||
|
||
### UNION ALL(保留重复)
|
||
|
||
```sql
|
||
SELECT city FROM employees
|
||
UNION ALL
|
||
SELECT city FROM former_employees;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------+
|
||
| city |
|
||
+------+
|
||
| 北京 |
|
||
| 上海 |
|
||
| 深圳 |
|
||
| 北京 |
|
||
| 广州 |
|
||
| 杭州 |
|
||
| 成都 |
|
||
| 北京 |
|
||
| 上海 |
|
||
| 深圳 |
|
||
| 广州 |
|
||
+------+
|
||
```
|
||
|
||
### 性能对比示例
|
||
|
||
```sql
|
||
-- 统计重复记录数量
|
||
SELECT
|
||
'UNION' AS query_type,
|
||
COUNT(*) AS record_count
|
||
FROM (
|
||
SELECT city FROM employees
|
||
UNION
|
||
SELECT city FROM former_employees
|
||
) AS union_result
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
'UNION ALL' AS query_type,
|
||
COUNT(*) AS record_count
|
||
FROM (
|
||
SELECT city FROM employees
|
||
UNION ALL
|
||
SELECT city FROM former_employees
|
||
) AS union_all_result;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------------+--------------+
|
||
| query_type | record_count |
|
||
+------------+--------------+
|
||
| UNION | 6 |
|
||
| UNION ALL | 11 |
|
||
+------------+--------------+
|
||
```
|
||
|
||
## 复杂查询示例
|
||
|
||
### 1. 条件过滤与UNION
|
||
|
||
查询高薪员工和所有管理层:
|
||
|
||
```sql
|
||
SELECT name, salary, '高薪员工' AS category
|
||
FROM employees
|
||
WHERE salary > 8000
|
||
|
||
UNION
|
||
|
||
SELECT name, salary, '管理层' AS category
|
||
FROM managers
|
||
|
||
ORDER BY salary DESC;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+------+----------+----------+
|
||
| name | salary | category |
|
||
+------+----------+----------+
|
||
| 陈总 | 15000.00 | 管理层 |
|
||
| 林总 | 12000.00 | 管理层 |
|
||
| 黄总 | 11000.00 | 管理层 |
|
||
| 张三 | 10000.00 | 管理层 |
|
||
| 王五 | 9000.00 | 高薪员工 |
|
||
| 孙八 | 8500.00 | 高薪员工 |
|
||
+------+----------+----------+
|
||
```
|
||
|
||
### 2. 聚合查询与UNION
|
||
|
||
各部门薪资统计:
|
||
|
||
```sql
|
||
SELECT department AS name, AVG(salary) AS avg_salary, '部门平均' AS type
|
||
FROM employees
|
||
GROUP BY department
|
||
|
||
UNION ALL
|
||
|
||
SELECT '公司总体' AS name, AVG(salary) AS avg_salary, '总体平均' AS type
|
||
FROM employees
|
||
|
||
UNION ALL
|
||
|
||
SELECT '管理层' AS name, AVG(salary) AS avg_salary, '管理平均' AS type
|
||
FROM managers
|
||
|
||
ORDER BY avg_salary DESC;
|
||
```
|
||
|
||
**结果:**
|
||
```
|
||
+----------+-------------+----------+
|
||
| name | avg_salary | type |
|
||
+----------+-------------+----------+
|
||
| 管理层 | 12000.00000 | 管理平均 |
|
||
| 技术部 | 8500.00000 | 部门平均 |
|
||
| 公司总体 | 7142.85714 | 总体平均 |
|
||
| 财务部 | 7000.00000 | 部门平均 |
|
||
| 市场部 | 6500.00000 | 部门平均 |
|
||
| 销售部 | 6000.00000 | 部门平均 |
|
||
| 人事部 | 5500.00000 | 部门平均 |
|
||
+----------+-------------+----------+
|
||
```
|
||
|
||
### 3. 多表复杂联合查询
|
||
|
||
创建完整的人员名册:
|
||
|
||
```sql
|
||
SELECT
|
||
id,
|
||
name,
|
||
department,
|
||
salary,
|
||
city,
|
||
'在职' AS status
|
||
FROM employees
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
id,
|
||
name,
|
||
department,
|
||
last_salary AS salary,
|
||
city,
|
||
'离职' AS status
|
||
FROM former_employees
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
id,
|
||
name,
|
||
'管理层' AS department,
|
||
salary,
|
||
region AS city,
|
||
'管理' AS status
|
||
FROM managers
|
||
|
||
ORDER BY status, salary DESC;
|
||
```
|
||
|
||
### 4. 使用子查询的UNION
|
||
|
||
查询每个城市的最高薪资员工:
|
||
|
||
```sql
|
||
SELECT name, city, salary, '当前最高薪' AS note
|
||
FROM employees e1
|
||
WHERE salary = (
|
||
SELECT MAX(salary)
|
||
FROM employees e2
|
||
WHERE e2.city = e1.city
|
||
)
|
||
|
||
UNION
|
||
|
||
SELECT name, city, last_salary AS salary, '前员工最高薪' AS note
|
||
FROM former_employees f1
|
||
WHERE last_salary = (
|
||
SELECT MAX(last_salary)
|
||
FROM former_employees f2
|
||
WHERE f2.city = f1.city
|
||
)
|
||
|
||
ORDER BY salary DESC;
|
||
```
|
||
|
||
## 最佳实践和注意事项
|
||
|
||
### 1. 性能优化建议
|
||
|
||
```sql
|
||
-- ❌ 避免:不必要的UNION,可以用OR替代
|
||
SELECT * FROM employees WHERE department = '技术部'
|
||
UNION
|
||
SELECT * FROM employees WHERE department = '销售部';
|
||
|
||
-- ✅ 推荐:使用OR条件
|
||
SELECT * FROM employees
|
||
WHERE department IN ('技术部', '销售部');
|
||
```
|
||
|
||
### 2. 数据类型兼容性
|
||
|
||
```sql
|
||
-- ✅ 正确:确保数据类型兼容
|
||
SELECT id, name, salary FROM employees
|
||
UNION
|
||
SELECT id, name, CAST(last_salary AS DECIMAL(10,2)) FROM former_employees;
|
||
|
||
-- ❌ 错误:数据类型不兼容可能导致错误
|
||
SELECT id, name, salary FROM employees
|
||
UNION
|
||
SELECT id, name, leave_date FROM former_employees; -- leave_date是DATE类型
|
||
```
|
||
|
||
### 3. 使用索引优化
|
||
|
||
```sql
|
||
-- 为UNION查询中的过滤条件创建索引
|
||
CREATE INDEX idx_employees_dept ON employees(department);
|
||
CREATE INDEX idx_employees_salary ON employees(salary);
|
||
CREATE INDEX idx_former_employees_dept ON former_employees(department);
|
||
|
||
-- 优化后的查询
|
||
SELECT name, department FROM employees WHERE department = '技术部'
|
||
UNION
|
||
SELECT name, department FROM former_employees WHERE department = '技术部';
|
||
```
|
||
|
||
### 4. 内存使用注意事项
|
||
|
||
```sql
|
||
-- 对于大型结果集,考虑使用LIMIT
|
||
SELECT name, salary FROM employees
|
||
UNION ALL
|
||
SELECT name, salary FROM managers
|
||
ORDER BY salary DESC
|
||
LIMIT 10;
|
||
```
|
||
|
||
### 5. 常见错误和解决方案
|
||
|
||
```sql
|
||
-- ❌ 错误:列数不匹配
|
||
SELECT name, department FROM employees
|
||
UNION
|
||
SELECT name FROM managers; -- 缺少一列
|
||
|
||
-- ✅ 修正:补齐列数
|
||
SELECT name, department FROM employees
|
||
UNION
|
||
SELECT name, level AS department FROM managers;
|
||
|
||
-- ❌ 错误:ORDER BY位置错误
|
||
SELECT name FROM employees ORDER BY name
|
||
UNION
|
||
SELECT name FROM managers ORDER BY name;
|
||
|
||
-- ✅ 修正:ORDER BY只能在最后
|
||
SELECT name FROM employees
|
||
UNION
|
||
SELECT name FROM managers
|
||
ORDER BY name;
|
||
```
|
||
|
||
### 6. 实用查询模式
|
||
|
||
#### 数据完整性检查
|
||
```sql
|
||
-- 检查是否有重复的员工记录
|
||
SELECT name, COUNT(*) as count
|
||
FROM (
|
||
SELECT name FROM employees
|
||
UNION ALL
|
||
SELECT name FROM former_employees
|
||
UNION ALL
|
||
SELECT name FROM managers
|
||
) AS all_people
|
||
GROUP BY name
|
||
HAVING COUNT(*) > 1;
|
||
```
|
||
|
||
#### 数据对比分析
|
||
```sql
|
||
-- 对比不同表中的数据分布
|
||
SELECT
|
||
'当前员工' AS source,
|
||
department,
|
||
COUNT(*) AS count,
|
||
AVG(salary) AS avg_salary
|
||
FROM employees
|
||
GROUP BY department
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
'前员工' AS source,
|
||
department,
|
||
COUNT(*) AS count,
|
||
AVG(last_salary) AS avg_salary
|
||
FROM former_employees
|
||
GROUP BY department
|
||
|
||
ORDER BY source, department;
|
||
```
|
||
|
||
---
|
||
|
||
**总结:**
|
||
- UNION 是合并查询结果的强大工具
|
||
- 注意列数、数据类型的匹配
|
||
- 根据需求选择 UNION 或 UNION ALL
|
||
- 合理使用索引和LIMIT提升性能
|
||
- 避免不必要的复杂UNION操作 |