UNION ALL 后如何高效去重(避免额外 DISTINCT)

1次阅读

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

UNION ALL 后如何高效去重(避免额外 DISTINCT)

UNION ALL 本身不支持去重,必须用 DISTINCT 或其他方式后置处理

UNION ALL 的设计目标就是零开销合并结果集,它连字段类型校验都只做基础兼容检查,更不会触发行级比较。所以“UNION ALL 后高效去重”本质上是个伪命题——你无法绕过去重所需的排序或哈希操作,但可以避开 DISTINCT 这个容易误用的“黑盒”。

用 GROUP BY 替代 DISTINCT,显式控制去重维度和性能边界

DISTINCT 看似简洁,实际会隐式对所有 select 列做全字段哈希/排序;而 GROUP BY 强制你声明去重依据,还能配合聚合函数保留有用信息。更重要的是,多数引擎(如 postgresqlmysql 8.0+、SQL Server)对 GROUP BY 的执行计划更可控,尤其当已有索引覆盖分组列时。

  • 如果只是去重整行,GROUP BY col1, col2, col3DISTINCT 效果一致,但可读性更强
  • 如果想保留某列最大值,直接写 MAX(updated_at),不用先 DISTINCT 再 JOIN 回原表
  • 避免 GROUP BY *(语法错误)或漏写非聚合列,否则报错或结果不可靠

在 UNION ALL 前预过滤,减少重复数据进入合并阶段

真正高效的去重不是“合并后再删”,而是“别让重复进来”。比如两个子查询分别查当天订单和历史补录订单,若补录逻辑已保证不会重复插入当天数据,就该在第二个子查询加 WHERE created_date ,而不是依赖后续去重。

  • 检查各分支的业务语义是否天然互斥(时间范围、状态码、来源标识等)
  • NOT EXISTSLEFT 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 BYORDER BY 列有联合索引,否则性能雪崩
  • PostgreSQL 中可考虑 DISTINCT ON (key) ORDER BY key, updated_at DESC,语义更清晰且通常更快

真正难的不是选 DISTINCT 还是 GROUP BY,而是得先搞清:这些“重复”是数据模型缺陷、etl 逻辑漏洞,还是查询视角不同导致的合理冗余。没理清这点,再花哨的 SQL 也只是把问题拖到执行层。

text=ZqhQzanResources