1. UNION与UNION ALL的本质区别
在SQL查询中,UNION和UNION ALL都是用于合并多个SELECT语句结果集的操作符,但它们的处理机制存在关键差异。理解这个差异对编写高效查询至关重要。
UNION ALL是最基础的集合合并操作,它简单地将两个结果集首尾相连,不做任何去重处理。假设我们有两个查询分别返回了100条和150条记录,使用UNION ALL合并后会得到250条记录。这种操作在数据库内部相当于数据追加,性能开销最小。
而UNION则在合并后会执行去重操作。同样的例子中,如果两个结果集有50条重复记录,使用UNION最终只会返回200条记录。这个去重过程实际上包含了以下步骤:
- 临时结果集创建
- 对所有列进行哈希计算或全列比较
- 重复记录过滤
- 最终结果返回
关键提示:UNION的去重是基于所有选定列的完整匹配,只要有一条记录中任意一列的值不同,就不会被视为重复。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 性能差异与执行计划分析
通过EXPLAIN命令分析这两种操作符的执行计划,可以直观看到性能差异。以MySQL为例:
UNION ALL的典型执行计划:
code复制-> Table scan on result1
-> Append
-> Table scan on result2
UNION的典型执行计划:
code复制-> Temporary table creation
-> Table scan on result1
-> Table scan on result2
-> Sort unique (using temporary table)
性能差异主要体现在:
- 临时表创建带来的I/O开销
- 排序/哈希计算消耗的CPU资源
- 内存使用量的显著增加
实测案例:在100万条记录的测试中,UNION ALL耗时0.8秒,而UNION需要4.3秒。当数据量达到1000万时,这个差距会扩大到5秒 vs 45秒。
3. 使用场景选择指南
3.1 必须使用UNION的情况
当业务逻辑要求排除重复记录时,例如:
- 合并多个数据源时确保结果唯一性
- 统计不重复用户数等去重场景
- 需要符合集合论中的数学定义时
sql复制-- 统计不同渠道的独立用户数
SELECT user_id FROM web_log
UNION
SELECT user_id FROM app_log
3.2 优先使用UNION ALL的情况
在以下场景应首选UNION ALL:
- 已知数据源无重复时
- 需要保留所有原始记录的审计场景
- 性能敏感的大数据量处理
- 中间结果需要进一步处理的ETL流程
sql复制-- 合并当天的订单和退货记录(允许重复)
SELECT * FROM today_orders
UNION ALL
SELECT * FROM today_returns
4. 高级应用技巧
4.1 与ORDER BY的配合使用
一个常见误区是试图在子查询中使用ORDER BY:
sql复制-- 错误写法
(SELECT * FROM table1 ORDER BY col1)
UNION
(SELECT * FROM table2 ORDER BY col2)
正确做法是在最终结果排序:
sql复制SELECT * FROM table1
UNION
SELECT * FROM table2
ORDER BY combined_column
4.2 数据类型处理规则
UNION操作要求各查询的列数相同,且对应列的数据类型必须兼容。当类型不完全相同时,数据库会按优先级进行隐式转换:
常见转换优先级:
- 浮点型 > 整型
- 字符串 > 日期
- 大类型 > 小类型
显式转换的最佳实践:
sql复制SELECT CAST(int_col AS VARCHAR), date_col FROM table1
UNION
SELECT string_col, CAST(varchar_date AS DATE) FROM table2
5. 常见问题解决方案
5.1 "Illegal mix of collations"错误
当字符集的校对规则(collation)不一致时会出现此错误。解决方案:
sql复制-- 方法1:统一指定collation
SELECT col COLLATE utf8mb4_unicode_ci FROM t1
UNION
SELECT col COLLATE utf8mb4_unicode_ci FROM t2
-- 方法2:修改连接默认collation
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
5.2 大数据量优化方案
对于千万级数据的UNION操作:
- 先使用WHERE过滤减少数据量
- 考虑分批次处理
- 使用临时表替代直接UNION
sql复制-- 优化方案示例
CREATE TEMPORARY TABLE temp_result AS
SELECT * FROM large_table1 WHERE create_date > '2023-01-01';
INSERT INTO temp_result
SELECT * FROM large_table2 WHERE create_date > '2023-01-01';
-- 后续操作基于临时表进行
SELECT DISTINCT * FROM temp_result;
6. 各数据库实现差异
6.1 MySQL/MariaDB特性
- 8.0版本后支持UNION的LIMIT下推优化
- 特有的"UNION DISTINCT"语法(与UNION等效)
6.2 SQL Server特性
- 支持TOP子句与UNION配合使用
- 提供UNION ALL的并行执行优化
6.3 PostgreSQL特性
- 支持UNION ALL的JIT编译优化
- 允许在UNION子查询中使用FETCH FIRST
在实际项目中,我通常会在开发阶段使用UNION ALL快速验证逻辑,在最终优化时再评估是否需要替换为UNION。对于ETL等批处理作业,99%的情况下UNION ALL都是更优选择。记住一个原则:除非业务明确需要去重,否则永远优先考虑UNION ALL。
