为什么 UNION
报错说列数不匹配?
最常见的是两个
SELECT返回的字段数量不一致,MySQL 会直接报错
ERROR 1222 (21000): The used SELECT statements have a different number of columns。这不是类型问题,是纯数量对不上。 检查每个
SELECT的
SELECT子句,数清楚逗号分隔的表达式个数(
*算作一个,但实际展开后必须和另一侧列数一致) 别依赖
SELECT *和
SELECT a,b,c混用 —— 表结构一旦变更,
*展开列数可能突变 如果真要补列,用
NULL或
''占位,但需注意类型隐式转换:比如
SELECT id, name FROM t1 UNION SELECT id, 0 FROM t2可能因
name是
VARCHAR而
0被转成字符串,但更稳妥的是显式写
CAST(0 AS CHAR)
UNION ALL
和 UNION
性能差十倍?
不是“差十倍”,而是去重逻辑本身开销巨大:
UNION需构建临时唯一哈希表或排序去重,而
UNION ALL只是追加结果集。尤其当结果行数过万、字段含长文本或无索引时,差异立刻暴露。 确认业务是否真的需要去重 —— 很多场景下,应用层 dedup 更可控(比如 Python 用
set(tuple(row) for row in results)) 如果必须用
UNION,确保参与查询的字段在各自表上有合适索引,减少中间结果体积 避免在
UNION外再套一层
ORDER BY—— MySQL 5.7+ 允许只在最后加
ORDER BY,但若提前加了子查询排序,可能触发额外临时表
调试时怎么看到 UNION
各部分的实际执行计划?
MySQL 的
EXPLAIN对集合操作支持有限:它只显示第一个
SELECT的执行计划,后续部分完全不展示。不能靠它判断第二段是不是全表扫描。 把每个
SELECT单独拎出来跑
EXPLAIN FORMAT=TREE(8.0+)或
EXPLAIN FORMAT=TRADITIONAL,逐个看
type、
rows、
Extra用
SELECT ... INTO OUTFILE或临时表分别保存各部分结果,对比行数和数据分布,快速定位哪一段膨胀得离谱 开启慢查询日志并设置
long_query_time = 0,抓取完整 SQL 执行耗时,再结合
SHOW PROFILE FOR QUERY N(需先
SET profiling = 1)看各阶段耗时占比
字符集冲突导致 UNION
直接失败?
错误信息通常是
ERROR 1267 (HY000): Illegal mix of collations。本质是两个结果集的字段用了不同校对规则(如
utf8mb4_0900_as_csvs
utf8mb4_general_ci),MySQL 无法自动统一比较逻辑。 查字段校对集:
SHOW FULL COLUMNS FROM table_name LIKE 'col_name',看
Collation列 强制统一:在对应
SELECT中用
COLLATE utf8mb4_0900_as_cs显式指定,例如
SELECT name COLLATE utf8mb4_0900_as_cs FROM t1长期解法是修改表/列默认校对集:
ALTER TABLE t1 MODIFY name VARCHAR(100) COLLATE utf8mb4_0900_as_cs,但需评估存量数据影响 实际排查时,最容易被跳过的环节是——没验证每个
SELECT单独执行是否真的返回预期结构和数据量。很多人一上来就改
UNION写法,却没发现其中一段 SQL 本身已因 JOIN 条件错误返回了几百万行。
