1. 组合查询(UNION)的本质与应用场景
在数据库操作中,我们经常遇到需要将多个查询结果合并输出的需求。比如电商平台需要同时展示"今日特价商品"和"会员专享商品",但这两类商品存储在不同的数据表中。这时候UNION操作符就派上用场了——它就像个数据管道工,把来自不同水源的数据流汇集成一条主干道。
我处理过的一个典型案例是银行交易系统:需要将储蓄账户流水和信用卡消费记录合并生成月度对账单。两种交易数据存储结构不同,但都需要以统一的格式呈现给客户。UNION在这里完美解决了多表数据整合的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. UNION的核心工作机制
2.1 基本语法结构
sql复制SELECT column1, column2 FROM table1
UNION [ALL]
SELECT column1, column2 FROM table2
2.2 执行流程解析
- 分别执行两个SELECT语句
- 自动去除重复行(除非使用UNION ALL)
- 按照第一个SELECT的列名输出结果
- 最终结果集的列数必须相同
关键细节:各SELECT语句对应位置的数据类型必须兼容。比如不能把日期型和整型直接UNION,需要先做类型转换。
3. 实际开发中的进阶用法
3.1 性能优化实践
在千万级数据表上使用UNION时,我总结出这些经验:
- 优先过滤再合并:WHERE条件应写在每个SELECT子句中
- 索引匹配原则:确保UNION字段都有索引
- 大数据量时考虑分页UNION(先各查50条再合并)
3.2 特殊场景处理
当需要保留表来源信息时:
sql复制SELECT 'Table1' AS source, id, name FROM employees
UNION
SELECT 'Table2' AS source, id, name FROM contractors
4. 典型问题排查指南
4.1 错误案例集锦
- 列数不一致报错:
sql复制-- 错误示例
SELECT id, name FROM users
UNION
SELECT id FROM orders -- 列数不匹配
- 隐式类型转换问题:
sql复制-- 错误示例
SELECT 'ID:' + CAST(id AS VARCHAR) FROM products
UNION
SELECT product_code FROM inventory -- 类型不兼容
4.2 性能问题诊断
遇到慢查询时检查:
- 是否所有子查询都走了索引
- 临时表大小是否合理
- 是否有不必要的排序操作
5. 与其他操作符的对比选择
5.1 UNION vs JOIN
- JOIN是横向扩展(增加列)
- UNION是纵向堆叠(增加行)
5.2 UNION vs UNION ALL
- UNION会去重且排序(性能开销大)
- UNION ALL直接合并(效率更高)
实际项目中,当确定数据无重复时,我总是优先使用UNION ALL。在某次物流系统优化中,改用UNION ALL使查询速度提升了3倍。
6. 最佳实践建议
- 明确业务需求:真的需要合并结果集吗?
- 小结果集优先:先过滤再UNION
- 考虑使用临时表:对复杂UNION可分步执行
- 监控执行计划:确保没有意外全表扫描
有次排查一个超时问题,发现开发者在UNION两侧都用了SELECT *,导致临时表爆内存。后来改为只查询必要字段,查询时间从15秒降到0.2秒。
7. 特殊数据库的实现差异
7.1 MySQL的特别之处
- 默认使用临时表实现UNION
- 8.0+版本支持LIMIT下推优化
7.2 Oracle的增强功能
- 支持UNION [DISTINCT]语法
- 可以使用ORDER BY配合ROWNUM分页
在最近的数据迁移项目中,我们发现Oracle的UNION对LOB字段的处理与MySQL有显著差异,不得不重写部分查询逻辑。
8. 真实业务场景解析
8.1 跨年数据统计案例
需要合并2022和2023的销售数据:
sql复制SELECT '2022' AS year, SUM(amount) FROM sales_2022
UNION
SELECT '2023' AS year, SUM(amount) FROM sales_2023
8.2 多条件筛选实现
替代复杂的OR条件:
sql复制SELECT * FROM products WHERE category = '电子'
UNION
SELECT * FROM products WHERE price > 1000
这种写法在索引利用上往往比一个大WHERE子句更高效。
