用 UNION ALL 合并多张结构一致的表再套子查询做跨表统计最安全高效;必须显式指定字段列表、保证顺序/类型/数量一致,子查询需起别名并 SELECT 所有外层需要的字段。

直接说结论:用 UNION ALL 合并多张结构一致的表,再套一层子查询做跨表统计,是安全、高效且兼容性最好的做法;但必须保证字段顺序、类型、数量完全一致,否则会报错或结果错乱。
UNION ALL 合并多表时字段对齐必须严格
很多人写完 SELECT * FROM t1 UNION ALL SELECT * FROM t2 就报错,根本原因是两张表字段数不等,或同位置字段类型冲突(比如 t1.col1 是 INT,t2.col1 是 VARCHAR)。数据库不会自动转换类型,也不会按列名匹配——它只认“第几个字段”。
- 必须显式写出字段列表,禁用
*,例如:SELECT id, name, amount FROM jan_sales UNION ALL SELECT id, name, amount FROM feb_sales - 如果某张表多一列(如
month),其他表也要补上常量值:SELECT id, name, amount, 'jan' AS month FROM jan_sales - 字符串长度差异大时(如
VARCHAR(10)vsVARCHAR(100)),建议统一转成更长的类型,避免截断:CAST(name AS VARCHAR(100))
子查询外层不能直接引用内层未 SELECT 的字段
这是嵌套聚合最常踩的坑。比如你想算所有月份销售额总和的平均值,写成 SELECT AVG(amount) FROM (SELECT id, name, amount FROM jan_sales UNION ALL SELECT id, name, amount FROM feb_sales) 是合法的;但若改成 SELECT AVG(amount), COUNT(DISTINCT name) ... 就会失败——因为子查询没选 name,外层就看不见它。
- 子查询必须把后续要用的所有字段都
SELECT出来,哪怕只是临时中转 - 子查询必须起别名(如
AS sales_all),否则 MySQL 8.0+ 和 SQL Server 会报语法错误 - 如果要加汇总维度(如按月份分组后再算均值),得在子查询里先
GROUP BY,再在外层聚合,不能跳步
UNION ALL + 子查询组合统计的真实场景
假设你有 jan_orders、feb_orders、mar_orders 三张月度订单表,每张都有 user_id、amount、status 字段,现在要统计:每个用户的总下单金额、订单数、以及这些用户平均订单金额。
正确写法是:
SELECT
AVG(total_amount) AS avg_user_order,
AVG(order_count) AS avg_order_per_user
FROM (
SELECT
user_id,
SUM(amount) AS total_amount,
COUNT(*) AS order_count
FROM (
SELECT user_id, amount FROM jan_orders WHERE status = 'paid'
UNION ALL
SELECT user_id, amount FROM feb_orders WHERE status = 'paid'
UNION ALL
SELECT user_id, amount FROM mar_orders WHERE status = 'paid'
) AS all_paid
GROUP BY user_id
) AS user_stats;注意三层结构:最内层 UNION ALL 拼数据 → 中间层 GROUP BY user_id 算单个用户指标 → 最外层对用户粒度结果再聚合。少任何一层,逻辑就断了。
真正容易被忽略的是:当某张源表为空时,UNION ALL 不影响结果,但如果你用了 UNION(去重),空表会导致整个查询变慢——因为数据库仍要走一遍去重逻辑。生产环境优先用 UNION ALL,去重交给业务层或外层 DISTINCT 显式控制。

















