UNION ALL必须用子查询或CTE包裹才能接GROUP BY;字段需严格对齐类型;WHERE条件须下推至各分支;GROUP_CONCAT在UNION ALL后可能因group_concat_max_len截断。

UNION ALL 必须包裹在子查询或 CTE 里才能接 GROUP BY
直接写 SELECT a FROM t1 UNION ALL SELECT a FROM t2 GROUP BY a 会报错,因为 SQL 解析器不认这种语法——UNION ALL 是集合操作符,优先级低于 GROUP BY,它不能作为 GROUP BY 的“数据源”直接出现。
正确做法只有两种:
- 用子查询:把整个
UNION ALL包进(...),再在外层SELECT ... FROM (...) AS src GROUP BY ... - 用 CTE:先
WITH src AS (SELECT ... UNION ALL SELECT ...),再SELECT ... FROM src GROUP BY ...
别指望数据库帮你自动推导执行顺序;不包一层,语法就过不了。
字段对齐必须显式声明,别信 NULL 自动适配
两个 SELECT 的列数、顺序、类型必须严格一致,否则 PostgreSQL 直接报错 UNION types text and integer cannot be matched,MySQL 可能隐式转但结果不可控。
常见错误包括:
- 一个分支有
remark字段,另一个没有 → 补NULL::VARCHAR,不是光写NULL - 金额字段一个是
DECIMAL,一个是INT→ 统一用CAST(amount AS DECIMAL) - 时间字段一个是
TIMESTAMP,一个是DATE→ 转成同一种类型,比如都::TIMESTAMP
别依赖数据库猜你想要什么类型;类型不一致的后果不是报错就是数值截断或精度丢失。
WHERE 条件必须下推到每个分支,不能只放外层
想查“近7天销售+退款”,如果只在外层加 WHERE dt >= CURRENT_DATE - 7,数据库得先把全量历史数据 UNION ALL 完再过滤,内存和 IO 爆炸。
正确写法是:
SELECT dt, amt FROM sales WHERE dt >= '2024-06-01' UNION ALL SELECT dt, amt FROM refunds WHERE dt >= '2024-06-01'- 每个分支的
WHERE要能命中各自表的索引,否则索引失效 - 别假设合并后还能走索引——合并完就是临时结果集,没索引
性能差往往不是因为 UNION ALL 本身慢,而是条件没下推导致扫描了不该扫的数据。
GROUP_CONCAT 在 UNION ALL 后被截断?调 group_concat_max_len
MySQL 中 GROUP_CONCAT 和 UNION ALL 一起用时,返回值常被截成 341 字符左右,这不是 bug,是 group_concat_max_len 默认值(1024 字节)在 UTF-8 下按三字节算的结果。
解决方式优先级建议:
- 业务层处理:让数据库返回多行,应用层拼接——最可控,避免 DB 层配置污染
- 会话级设置:
SET SESSION group_concat_max_len = 1000000,适合临时脚本 - 全局配置:改
my.cnf或SET GLOBAL,但要注意max_allowed_packet限制实际上限
注意:这个截断只发生在 GROUP_CONCAT 作用于 UNION ALL 结果集时,单表查询不会触发——问题根源是合并后结果集变大,而参数没同步调高。

















