1. 什么时候真正需要UNION:从单表拆分的实际场景说起
我最早对UNION产生强烈印象,是在处理一套订单数据的时候。业务那边给了个需求:要把VIP用户的历史订单和普通用户的订单放在同一个报表里统计。当时第一反应是用JOIN,折腾了半天发现JOIN是把两张表的列横向拼起来,而需求是要把两个数据集上下堆叠,逻辑完全是两回事。后来才意识到,UNION才是这类场景的正解。
UNION的本质是把多个SELECT语句的查询结果纵向合并成一个结果集,每个SELECT返回的列数必须一致,列的数据类型要兼容,列的顺序也要对齐。听起来简单,但实际使用中,很多人连“什么时候该用UNION”都没想清楚就直接写,最后要么结果错了,要么性能很糟糕。
我梳理过几种典型场景,基本都是UNION的“甜点区”:
- 历史数据归档:比如订单主表只保留最近三个月的数据,超过三个月的归档到历史表。但报表又必须统计全量,这时候把主表的结果和历史表的结果用UNION合并,比改业务代码更干净。
- 同构多源数据合并:多个分库的表结构一致,想把各分库的数据汇总到一起分析,UNION是天然的选择。
- 同一张表内不同维度的汇总:比如按“今日新增用户”和“今日活跃用户”分别统计,再用UNION拼成一个结果集,放在一张报表里展示。
- 字典值补全或类型映射:某个字段在主表中没有对应记录,但业务上希望空值也能显示成一行“未分类”,这时可以用UNION追加一个固定值的SELECT。
需要特别提醒的是:能在一个SELECT里用CASE WHEN或条件聚合解决的,就不要用UNION。UNION并不是越多越好,它意味着你要对多张表或多段逻辑分别执行扫描,然后合并,成本是叠加的。如果只是为了加一列值,用条件判断往往更高效。
我见过不少人在应该用UNION ALL的时候写了UNION,导致全表数据被拉去内存做去重,结果数据量一大直接爆内存或慢查询超时。这个去重成本是很隐蔽的,下一节我会专门展开讲。
简单总结一下:UNION解决的是“多个结果集纵向拼接”的问题,使用前提是列数一致、类型兼容、顺序对齐。判断标准就一句话——你需要的是一张“变长的表”,而不是“变宽的列”。搞清楚这一点,后续的细节才有意义。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. UNION与UNION ALL:性能取舍的账其实不难算
很多新手分不清UNION和UNION ALL的区别,其实就一个关键点:UNION会去除重复行,UNION ALL不会。就是这个“去重”,会让两者在性能和语义上产生天壤之别。
打个比方:UNION相当于先把两份名单贴在一起,然后拿笔把所有重复的名字划掉,只留一个;UNION ALL则是直接用订书机把两份名单钉在一起,有多少算多少。显然,前者多了一道“核对去重”的工序,这道工序就是性能开销的主要来源。
我在一个实际项目里验证过这个差距。有一张用户行为表,量级在千万级别,我用UNION把两个分段查询拼起来,执行时间大概在4秒左右;换成UNION ALL之后,执行时间一下子降到了1秒出头。数据集越大、重复率越低,这个差别就越明显。原因很简单:UNION要去重,就必须把两个结果集都拉到临时表里,做排序或哈希去重,这个操作是额外的内存和CPU消耗。
所以在选择时,我的判断逻辑是这样的:
- 如果两个查询结果从业务逻辑上不可能重复,比如一个是A渠道的订单,一个是B渠道的订单,且每条订单都有唯一ID不会跨界,直接用UNION ALL。
- 如果允许重复出现,或者重复对业务统计没有影响,用UNION ALL。
- 只有当业务上确实要求“合并后每个记录只出现一次”时,才用UNION。
有人可能会说:“用UNION保险一点,万一重复了呢?”这句话在小型项目里没毛病,但在数据量大一点的线上环境,这个“保险”可能直接让查询变慢一个数量级。与其用UNION兜底,不如先在业务上想清楚两个子查询的数据是否天然互斥。
另外还有一个常见的误用场景:在UNION后面的每个子查询里明明已经用DISTINCT了,外层再用UNION,这就等于做两次去重,纯属浪费。正确的做法是:每个子查询内不要写DISTINCT,把去重交给外层的UNION一次完成;如果用了UNION ALL,那子查询内的去重就有必要。
还有个细节容易被忽略:UNION结果集的列名是以第一个SELECT的列名为准。这意味着第二个SELECT可以给列起别名,但不会影响最终结果集的列名。如果应用层代码是按列名取值的,就要以第一个SELECT的列名为准来映射。
我见过有人为了让报表显示的表头好看,在最后一个SELECT里改了别名,结果发现前端根本不认。这个不算大坑,但调试的时候容易让人懵。记住一点:第一段SELECT决定列名和数据类型,后面的SELECT只需要保证列数和类型兼容就行。
3. 类型不一致、ORDER BY和LIMIT的执行顺序坑
UNION用多了就会发现,坑往往不在UNION本身,而在与其它子句组合时产生的“化学反应”。我挑几个踩得最多、也最隐蔽的来说。
3.1 列类型不匹配:隐式转换在悄悄改变你的数据
UNION要求各
