union all 替代 group by + union 可显著提升聚合报表效率,因其避免去重排序、减少全表扫描,且需严格保证各子查询字段数量、顺序、类型一致并显式补 null。

直接用 UNION ALL 替代多段 GROUP BY + UNION 拼接,是提升复杂条件聚合报表执行效率最立竿见影的手段——它省掉去重排序开销,避免重复扫描,还能让 SQL 更易维护。
为什么 UNION ALL 比 UNION 快得多
数据库对 UNION 的默认行为是去重,这背后要触发排序或哈希去重操作;而 UNION ALL 只是把结果集顺序拼接,不检查重复、不排序、不建临时哈希表。在千万级销售明细上,两者执行时间可能差 3–5 倍。
-
UNION隐含DISTINCT逻辑,即使你明确知道各子查询结果互斥,数据库也照做一遍去重 - 一旦加上
ORDER BY或LIMIT,UNION还会强制对整个合并后结果排序,开销进一步放大 - 很多报表场景(比如按月分表汇总)天然无重复主键,用
UNION纯属浪费资源
多粒度汇总时,UNION ALL 怎么替代传统 GROUP BY + UNION 写法
典型场景:一张销售明细表,要同时输出「产品线总销售额」「区域总销售额」「产品线×区域组合」「全局总计」四层口径。传统写法靠五段 GROUP BY + UNION ALL 拼接,但每段都要全表扫描一次。
更优解是先用 GROUPING SETS(如果数据库支持),否则就用 UNION ALL 显式控制每段聚合范围,并确保字段对齐:
SELECT product_category, region, NULL AS sales_month, SUM(revenue) AS revenue FROM sales_summary GROUP BY product_category, region UNION ALL SELECT product_category, NULL, NULL, SUM(revenue) FROM sales_summary GROUP BY product_category UNION ALL SELECT NULL, region, NULL, SUM(revenue) FROM sales_summary GROUP BY region UNION ALL SELECT NULL, NULL, NULL, SUM(revenue) FROM sales_summary;
- 所有子查询必须显式写出相同数量、相同顺序、类型兼容的字段,不能用
* - 补
NULL占位不是可选项——这是保证列对齐和后续ORDER BY正常工作的前提 - 字段名以第一个子查询为准,后续子查询中同位置字段类型若不兼容(如
VARCHAR(10)和VARCHAR(200)),建议统一CAST
嵌套聚合里用 UNION ALL 合并多张月度表,最容易踩的三个坑
比如有 jan_sales、feb_sales、mar_sales 三张结构一致的表,想算每个用户的总金额和平均订单额,常见错误不是语法报错,而是结果错乱。
- 字段顺序/数量不一致:
SELECT user_id, amount FROM jan_sales UNION ALL SELECT user_id, status FROM feb_sales会把status当作amount,数值类型隐式转换失败或截断 - 子查询没选够字段:
SELECT AVG(amount) FROM (SELECT user_id FROM jan_sales UNION ALL SELECT user_id FROM feb_sales) t——外层要算AVG(amount),但内层根本没查amount,直接报错 - 忘了起别名:
SELECT * FROM (SELECT id, amt FROM t1 UNION ALL SELECT id, amt FROM t2)在 PostgreSQL 和 MySQL 8.0+ 中会报 “every derived table must have its own alias”
真正关键的不是“能不能写出来”,而是字段对齐是否严格、NULL 占位是否到位、子查询是否把外层要用的所有字段都 SELECT 出来——这些细节一错,结果就不可信,而问题往往藏在几十行 SQL 的中间某处,很难一眼发现。










