union all 优先级低于 join,因此 select from t1 union all select from t2 join t3 on ... 会报错;必须用括号或子查询明确包裹 union all 结果,如 (select ... union all select ...) as u join ...。

UNION ALL 之后不能直接 JOIN?先搞清执行顺序
SQL 中 UNION ALL 是集合操作,优先级低于 JOIN。写成 SELECT * FROM t1 UNION ALL SELECT * FROM t2 JOIN t3 ON ... 会报错或语义错误——解析器会尝试把 UNION ALL 的右半部分当成 JOIN 的左表,根本不是你想要的“先合并再关联”。必须用括号或子查询明确包裹 UNION ALL 结果。
正确做法是把它当做一个临时结果集:
SELECT u.*, o.order_time FROM ( SELECT id, user_id, amount FROM order_202301 UNION ALL SELECT id, user_id, amount FROM order_202302 UNION ALL SELECT id, user_id, amount FROM order_202303 ) AS u JOIN orders_user o ON u.user_id = o.id;
分表字段名/类型不一致时 UNION ALL 会静默失败或出错
UNION ALL 要求所有子查询列数相同、对应位置列类型兼容(如 MySQL 会尝试隐式转换,PostgreSQL 更严格)。如果某张分表里 amount 是 DECIMAL,另一张是 INT,PostgreSQL 直接报 UNION types integer and numeric cannot be matched;MySQL 可能转成浮点但精度丢失。
- 检查每张分表的
DESCRIBE table_name,确认列名拼写、顺序、类型完全一致 - 显式写出列名,避免用
*(尤其当分表加了新字段但未同步时) - 对类型差异列用
CAST(... AS DECIMAL(10,2))统一强转
JOIN 大结果集性能崩了?别让 UNION ALL 成为瓶颈
把几十张分表 UNION ALL 出来再 JOIN,等价于先扫描全部分表数据、拼成一张超大中间表、再走嵌套循环或哈希连接——内存爆、IO 高、执行计划失效。这不是语法问题,是数据规模和索引设计问题。
更可行的路径:
- 在每张分表上分别
JOIN,再UNION ALL结果(利用分表本地索引) - 用
IN (subquery)或EXISTS替代JOIN,减少中间数据量 - 确认分表键(如
user_id)在各分表和关联表上都有索引 - PostgreSQL 可考虑
UNION ALL后加LIMIT+OFFSET分页,但需注意偏移成本
MySQL 8.0+ 支持 CTE,但别滥用 WITH 子句包装 UNION ALL
有人写成:
WITH merged AS ( SELECT id, user_id FROM order_202301 UNION ALL SELECT id, user_id FROM order_202302 ) SELECT * FROM merged JOIN users u ON merged.user_id = u.id;
看起来清晰,但 MySQL 8.0 对 CTE 的物化策略默认是 NO MERGE,即先执行完整 UNION ALL 再 JOIN,没带来优化,反而多一层解析开销。除非配合 MATERIALIZED 提示且后续多次引用,否则不如直接子查询。
真正省事又可控的方式,还是带别名的内联子查询——它强制优化器按你写的顺序执行,也方便加 WHERE 下推(比如在每个 SELECT 子句里加 WHERE create_time >= '2023-01-01')。
分表合并本身不难,难的是合并后还能高效关联。字段对齐、执行顺序、索引覆盖、引擎特性,漏掉哪一环都可能让查询从秒级变分钟级。










