union要求字段数量严格一致、类型兼容且按位置匹配,错误会导致报错或数据错位;应使用null占位、cast统一类型、优先union all提升性能,并将order by和limit置于整个语句末尾。

UNION 不是万能拼接工具,用错会直接报错或数据错位——关键在字段对齐、类型兼容和去重逻辑。
UNION 报错“Column count doesn’t match”怎么办
这是最常卡住人的第一步:所有 SELECT 语句的字段数量必须严格一致。MySQL 不会自动补空或忽略列。
- 错误写法:
SELECT id, name FROM usersUNIONSELECT id FROM logs→ 少一列,直接报错 - 正确做法:用
NULL或默认值占位,保持列数对齐,例如:SELECT id, name, NULL AS city FROM usersUNIONSELECT id, '' AS name, city FROM addresses - 注意别名只影响最终结果集的列名,不解决数量 mismatch;真正起作用的是第一个
SELECT的字段顺序和个数,后续所有子句都必须向它看齐
字段类型不兼容导致数据被截断或隐式转换
对应位置的字段类型不一致时,MySQL 会尝试隐式转换,但容易出问题:比如 VARCHAR(10) 和 INT 混用,可能把数字转成字符串再截断,或反过来把字符串转成 0。
- 典型风险:第一个
SELECT返回age INT,第二个返回'unknown' VARCHAR→ MySQL 强制转成整数,结果变成 0 - 稳妥做法:显式用
CAST()或CONVERT()统一类型,例如:SELECT CAST(age AS CHAR) FROM studentsUNIONSELECT name FROM teachers - 时间类型尤其敏感:
DATETIME和DATE虽然能 union,但精度丢失(秒部分被清零),需提前DATE()或CAST(... AS DATETIME)对齐
UNION vs UNION ALL 性能差十倍以上
UNION 默认等价于 UNION DISTINCT,MySQL 必须对全部结果做排序 + 去重,数据量一过几万行,响应就明显变慢;UNION ALL 是纯追加,几乎无开销。
- 只要业务允许重复(比如日志归档、分表汇总、导出原始数据),优先用
UNION ALL - 想手动去重又不想扛性能压力?先用
UNION ALL拼好,再套一层SELECT DISTINCT—— 但注意:这仍然比原生UNION慢,因为去重发生在更晚阶段 - 如果最后要
ORDER BY,务必放在整个 UNION 语句末尾,且只能引用第一个SELECT的列名或序号,例如:(SELECT a,b FROM t1) UNION ALL (SELECT a,b FROM t2) ORDER BY a
ORDER BY 和 LIMIT 只能写在最后,不能每个子句单独加
你不能写 SELECT * FROM t1 ORDER BY id LIMIT 10 UNION SELECT * FROM t2 ORDER BY id LIMIT 10 —— 这语法非法,MySQL 直接报错。
- 正确方式:把每个子句用括号包住(虽非强制,但强烈建议),然后统一在末尾加
ORDER BY和LIMIT,例如:(SELECT id,name FROM t1) UNION ALL (SELECT id,name FROM t2) ORDER BY id LIMIT 20 - 如果真需要分别限制各子集的数据量(比如“取每张表最新 5 条”),必须用派生表:
SELECT * FROM (SELECT * FROM t1 ORDER BY created_at DESC LIMIT 5) t1_top UNION ALL SELECT * FROM (SELECT * FROM t2 ORDER BY created_at DESC LIMIT 5) t2_top - 别指望靠
ORDER BY控制中间结果顺序:UNION 不保证子句执行顺序,也不保留子句内的排序,只有最终的ORDER BY有效
最容易被忽略的其实是字段对齐的“隐形契约”:UNION 不校验语义,只按位置硬匹配。name 列后面跟了个 age,那第二条 SELECT 的第二列不管叫 salary 还是 birth_year,都会被当成 age 处理——错位了也没警告,只默默出错数据。











