UNION ALL 本身不支持去重,需用 GROUP BY 替代 DISTINCT 实现高效可控去重,并通过预过滤减少重复数据进入合并阶段。

UNION ALL 本身不支持去重,必须用 DISTINCT 或其他方式后置处理
UNION ALL 的设计目标就是零开销合并结果集,它连字段类型校验都只做基础兼容检查,更不会触发行级比较。所以“UNION ALL 后高效去重”本质上是个伪命题——你无法绕过去重所需的排序或哈希操作,但可以避开 DISTINCT 这个容易误用的“黑盒”。
用 GROUP BY 替代 DISTINCT,显式控制去重维度和性能边界
DISTINCT 看似简洁,实际会隐式对所有 SELECT 列做全字段哈希/排序;而 GROUP BY 强制你声明去重依据,还能配合聚合函数保留有用信息。更重要的是,多数引擎(如 PostgreSQL、MySQL 8.0+、SQL Server)对 GROUP BY 的执行计划更可控,尤其当已有索引覆盖分组列时。
- 如果只是去重整行,
GROUP BY col1, col2, col3和DISTINCT效果一致,但可读性更强 - 如果想保留某列最大值,直接写
MAX(updated_at),不用先DISTINCT再 JOIN 回原表 - 避免
GROUP BY *(语法错误)或漏写非聚合列,否则报错或结果不可靠
在 UNION ALL 前预过滤,减少重复数据进入合并阶段
真正高效的去重不是“合并后再删”,而是“别让重复进来”。比如两个子查询分别查当天订单和历史补录订单,若补录逻辑已保证不会重复插入当天数据,就该在第二个子查询加 WHERE created_date ,而不是依赖后续去重。
- 检查各分支的业务语义是否天然互斥(时间范围、状态码、来源标识等)
- 用
NOT EXISTS或LEFT JOIN ... IS NULL挡住已存在记录,比合并后去重快一个数量级 - 注意:提前过滤可能增加单个子查询复杂度,需用
EXPLAIN对比实际执行计划
大数据量下慎用窗口函数 ROW_NUMBER() + WHERE rn = 1
当需要按某个优先级取“第一条”而非简单去重(例如保留最新更新的记录),ROW_NUMBER() OVER (PARTITION BY key ORDER BY updated_at DESC) 是常见解法。但它会强制全局排序,内存和 CPU 开销远高于 GROUP BY,且无法利用索引加速分区。
- 仅在必须保序取首行时使用,不要当成
DISTINCT的替代品 - 确保
PARTITION BY和ORDER BY列有联合索引,否则性能雪崩 - PostgreSQL 中可考虑
DISTINCT ON (key) ORDER BY key, updated_at DESC,语义更清晰且通常更快
DISTINCT 还是 GROUP BY,而是得先搞清:这些“重复”是数据模型缺陷、ETL 逻辑漏洞,还是查询视角不同导致的合理冗余。没理清这点,再花哨的 SQL 也只是把问题拖到执行层。










