SQL中Union语句优化
当Union成为SQL中影响效率的核心问题,需优化Union语句提高SQL效率
Union语句优化
- 先问“要不要去重”——Union → Union ALL
- Union 隐含
DISTINCT,需要对最终结果再做一次 排序-去重(内存或磁盘文件排序)。 - Union ALL 只是简单拼接,省去排序,CPU & IO 直接减半甚至一个数量级。
适用:业务上允许出现重复行,或上游已保证唯一。
示例:
1 | -- 原句 |
- 各分支单独“早过滤”——把 WHERE 下推到分支
错误写法(先合并再过滤):
1 | SELECT * FROM ( |
正确写法(每个分支自带 WHERE):
1 | SELECT * FROM orders_2023 WHERE order_date >= '2024-01-01' |
效果:扫描分区量减少,可能直接走索引范围扫描。
- 只选需要的列——杜绝 SELECT *
- 列越少,中间临时表越小,排序 / Hash 去重内存占用越低。
- 对覆盖索引更友好(ICP、索引-only 扫描)。
- 让“各分支结果集”提前排序 → 利用 Merge Append
PostgreSQL 的 MergeAppend 路径可以在 各分支已按同一列排序 的情况下,直接顺序归并,避免额外 Sort 节点。
示例:
1 | SELECT user_id, SUM(amount) FROM ( |
Explain 可见:Merge Append 而非 Sort + Append。
- 给“去重列”建索引——让排序变索引顺序
如果 必须用 Union(去重),可对 去重涉及的列 建 联合索引,优化器会优先选择 索引顺序扫描(INDEX ORDER BY),减少外部排序。
MySQL 示例:
1 | CREATE INDEX uk_order_id ON orders_2023(order_id); |
Explain Extra 从 Using temporary; Using filesort → Using index。
- 分页场景“下推 + 限制”
OLAP 报表常用:
1 | (SELECT * FROM A ORDER BY id LIMIT 50000) |
原理:每个分支先取 Top-N,再全局归并,避免把全表数据拖进临时表。
- 并行执行——开启并行 Union 分支
- PostgreSQL:
set max_parallel_workers_per_gather = 8;并行 Append 已原生支持。 - SQL Server:
OPTION (MAXDOP 8); - MySQL 8.0:目前无并行 Union,但可 手动拆成多连接 + 应用层归并(高并发场景)。
- 物化视图 / 中间表——空间换时间
对 超大历史分区 + 频繁报表 场景,可 增量物化:
1 | CREATE MATERIALIZED VIEW mv_orders AS |
查询直接 SELECT ... FROM mv_orders,秒级响应。
- 改写为“分区表”——从根本上消灭 Union
MySQL 8.0 / PostgreSQL 12+ 均支持 原生分区(RANGE/LIST/HASH),优化器可直接 分区剪枝,再也无需手写 Union ALL。
示例(MySQL):
1 | CREATE TABLE orders ( |
查询:
1 | SELECT * FROM orders WHERE order_date >= '2024-01-01'; |
自动只扫 p2024 分区,与手写 Union ALL 等价,但 零维护成本。
快速 checklist
| 检查项 | 预期结果 |
|---|---|
| 业务允许重复? | 用 Union ALL |
| 每个分支是否带 WHERE? | 行数减半 |
| 是否 SELECT 只需要的列? | 临时表更小 |
| 去重列是否有索引? | 避免 filesort |
| 能否改分区表? | 彻底告别 Union |
按表逐项验证,**90% 的 Union 性能问题可在一轮索引+Union ALL 改造后下降 50%~80%**。
实际案例
修改前
1 | select '实销完成率' AS type, |
主要优化点:
- 预计算和过滤:添加了
tmp_combat_filteredCTE,提前计算所有三个指标并过滤数据,避免重复计算 - 使用UNION ALL:将UNION改为UNION ALL,避免去重开销
- 减少重复计算:在预处理阶段计算好所有需要的字段
- 保持结构清晰:将复杂的计算逻辑集中处理,使主查询更简洁
这样优化后,下面的UNION部分应该会快很多,因为:
- 避免了重复的过滤条件计算
- 减少了数据扫描次数
- UNION ALL比UNION效率更高
tmp_area保持原样,因为它不影响性能。
修改后
1 | WITH tmp_combat_filtered AS ( |
| 匹配到优化点 | 说明 |
|---|---|
| 1. Union → Union ALL | 把三条 UNION 改成 UNION ALL 再横向转列,实质彻底消灭了去重排序。 |
| 2. 早过滤(WHERE 下推) | 星级条件 <if test="star..."> 在 CTE 第一步就生效,三张虚拟表共享同一份过滤结果。 |
| 3. 减少列宽度 | 原语句三次子查询都 SELECT *,改写后只取出后续要用的 10 余个指标列,不再拖整行。 |
| 4. 排序一次复用 | 原语句每条指标单独 RANK() OVER (ORDER BY rate DESC),共 3 次排序;改写后仅对三张“宽列”做一次窗口函数,三条指标同时拿到 rk_sxwcl / rk_sxzzl / rk_nevzzl,相当于 Merge Append 复用排序。 |
- 标题: SQL中Union语句优化
- 作者: Sabthever
- 创建于 : 2025-11-12 10:37:53
- 更新于 : 2025-11-13 16:40:42
- 链接: https://sabthever.cn/2025/11/12/technology/bigdata/SQL中Union语句优化/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。