浅析MySQL查询去重是使用UNION还是DISTINCT

 更新时间:2026年07月24日 08:33:25   作者:detayun  
本文对比分析了MySQL中去重操作DISTINCT与UNION/UNION ALL的核心差异,文内将彻底讲透为什么UNION ALL+DISTINCT比UNION更快,以及单表去重、多表合并等不同场景下的最佳选型,帮你避免线上慢查询和性能故障

前言

开发过程中经常面临数据去重需求,大家常会纠结两种方案:

  1. 使用 DISTINCT 在单条结果集中完成去重;
  2. 使用 UNION 合并多条查询并自动去重。

还有很多开发者分不清 UNIONUNION ALL 的巨大差异,经常误用导致数据库出现不必要的性能消耗。

本文对比两者原理、适用场景、性能差距,给出线上环境选型标准。

一、先理清基础语法与核心行为

1. DISTINCT

作用:对单条SQL的结果集进行去重

SELECT DISTINCT user_id FROM `user_login_log` WHERE `date` = CURDATE();

执行逻辑:数据库取出所有满足条件的数据,按照指定字段进行分组对比,剔除重复行,保留唯一记录。

2. UNION 与 UNION ALL(重点区分)

-- UNION:合并结果 + 自动去重 + 排序
SELECT user_id FROM `user` WHERE status = 1
UNION
SELECT user_id FROM `app_key` WHERE status = 1;

-- UNION ALL:仅简单纵向拼接,**不去重、不排序**
SELECT user_id FROM `user` WHERE status = 1
UNION ALL
SELECT user_id FROM `app_key` WHERE status = 1;

很多人踩坑:以为 UNION = UNION ALL,二者性能差距极大。

UNION = UNION ALL + DISTINCT + 排序操作

二、底层实现原理对比

DISTINCT 原理

在结果集内部构建临时内存哈希表或者排序缓冲区,遍历数据消除本行内重复记录。

数据量较小使用内存;数据量大超过缓冲区限制,则落地磁盘临时文件,性能断崖下跌。

UNION 原理

  1. 分别执行前后两条子查询;
  2. 使用 UNION ALL 把所有数据纵向汇总;
  3. 全局执行一次DISTINCT排序去重

简单公式:

UNION = UNION ALL + DISTINCT

三、核心性能结论

  1. 如果业务需要合并多条SQL结果并且去重:可以使用 UNION
  2. 如果多条SQL合并,原始数据不存在重复,优先使用 UNION ALL,不要用 UNION;
  3. 如果只是单表/单条查询内部去重,不要使用 UNION,直接使用 DISTINCT
  4. 杜绝滥用 UNION 实现单条SQL内部去重,属于完全错误用法。

四、场景分类实战分析

场景1:单条查询内部去除重复数据

✅ DISTINCT

需求:查询当日登录日志里所有活跃用户ID,同一用户多条登录记录只展示一次。

-- 正确写法
SELECT DISTINCT user_id FROM user_login_log WHERE `date` = CURDATE();

-- ❌ 错误示范,没必要强行拆分UNION
SELECT user_id FROM user_login_log WHERE `date` = CURDATE()
UNION
SELECT user_id FROM user_login_log WHERE `date` = CURDATE();

强行使用UNION会执行两次相同查询,扫描双倍数据,额外执行全局去重,资源翻倍浪费。

场景2:多条独立查询结果合并,需要全局去重

需求:从用户表、密钥表两处查询user_id,合并结果,同一个user_id只保留一条。

方案A UNION

SELECT user_id FROM `user` WHERE username = 'demo'
UNION
SELECT user_id FROM `app_key` WHERE access_key = 'demo_key';

方案B UNION ALL + 外层DISTINCT

SELECT DISTINCT user_id FROM (
    SELECT user_id FROM `user` WHERE username = 'demo'
    UNION ALL
    SELECT user_id FROM `app_key` WHERE access_key = 'demo_key'
) t;

重点:方案A 和方案B哪个更快?

绝大多数情况下:UNION ALL + 外层DISTINCT 性能 ≥ UNION

原因:

UNION默认会附带排序行为;

而外层DISTINCT优化器可以选择哈希去重,不一定强制排序,优化空间更大。

追求稳定高性能,推荐统一使用 UNION ALL + DISTINCT 写法,避免UNION隐性排序带来开销。

场景3:多条查询合并,明确不存在重复数据

直接使用 UNION ALL不要使用UNION,不要额外加DISTINCT

省去全局比较、排序、去重的巨大开销。

SELECT id FROM `user` LIMIT 100
UNION ALL
SELECT id FROM `app_key` LIMIT 100;

五、高频误区汇总

误区1:UNION 和 DISTINCT 可以随意互相替换

不能替换。

DISTINCT作用于单查询内部

UNION作用于多条查询合并之后,适用场景边界完全不同。

误区2:UNION去重性能优于 UNION ALL + DISTINCT

恰恰相反。

UNION强制执行排序去重;UNION ALL只做拼接,把去重选择权交给外层,优化器拥有更多优化策略。

误区3:少量数据,随便写无所谓

在测试环境少量数据看不出差距;当结果集上万、十万级别,UNION额外排序会直接引发慢查询,线上极易爆出性能故障。

误区4:不知道UNION自带排序,导致不必要的消耗

MySQL UNION规范:合并完成后会执行排序操作;如果你不需要排序,不要使用UNION。

六、索引层面额外优化提示

DISTINCT查询尽量建立覆盖索引,避免大量回表;

-- 示例:利用索引直接完成去重,无需读取原始数据表
CREATE INDEX idx_date_user ON user_login_log(`date`,user_id);

UNION ALL拆分多条查询时,每条子查询务必保证可以正常命中索引;

如果最终只需要获取第一条匹配记录,可以每层子查询增加 LIMIT 实现短路查询,减少扫描行数。

七、选型决策清单(线上直接套用)

  1. 单条SQL内部去重 → 使用 DISTINCT
  2. 多条SQL结果合并,存在重复且需要去重 → 优先:UNION ALL + 外层DISTINCT
  3. 多条SQL结果合并,确认无重复 → 使用 UNION ALL
  4. 禁止:单条查询场景强行拆分使用UNION做去重
  5. 禁止:能用UNION ALL的场景随意使用UNION

八、验证手段

使用 EXPLAIN 观察执行计划:

  • UNION:通常能看到 Using temporary; Using filesort(临时表+文件排序)
  • UNION ALL:没有全局排序与临时表,执行计划更加简洁

总结一句话:去重工具没有绝对好坏,分清场景再选择;能使用 UNION ALL 就不要使用 UNION,能避免全局排序就尽量避免。

延伸业务小案例(你项目常用场景)

根据账号、邮箱、密钥多条件检索用户ID:

-- 最优写法
SELECT DISTINCT user_id FROM (
    SELECT user_id FROM `user` WHERE username = 'demo'
    UNION ALL
    SELECT user_id FROM `user` WHERE email = 'demo@test.com'
    UNION ALL
    SELECT user_id FROM `app_key` WHERE access_key = 'demo_key'
) tmp;

相比直接写三条UNION,性能更好,也是线上检索场景标准写法。

到此这篇关于浅析MySQL查询去重是使用UNION还是DISTINCT的文章就介绍到这了,更多相关MySQL查询去重内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL连接异常报10061错误问题解决

    MySQL连接异常报10061错误问题解决

    这篇文章主要介绍了MySQL连接异常报10061错误问题解决,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • MySQL5.7.18修改密码的方法

    MySQL5.7.18修改密码的方法

    这篇文章主要介绍了MySQL5.7.18修改密码的方法,非常不错,具有参考解决价值,需要的朋友可以参考下
    2017-05-05
  • 关于MySQL绕过授予information_schema中对象时报ERROR 1044(4200)错误

    关于MySQL绕过授予information_schema中对象时报ERROR 1044(4200)错误

    这篇文章主要介绍了关于MySQL绕过授予information_schema中对象时报ERROR 1044(4200)错误,本文给大家分享解决方法,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • MySQL启动失败报错:mysqld.service failed to run ‘start-pre‘ task的问题分析与解决方案

    MySQL启动失败报错:mysqld.service failed to run 

    在日常运维中,MySQL 作为广泛应用的关系型数据库,其稳定性和可用性至关重要,然而,有时系统升级或配置变更后,MySQL 服务可能会出现无法启动的问题,本文针对某次实际案例进行深入分析和处理,需要的朋友可以参考下
    2024-12-12
  • MYSQL中information_schema的使用

    MYSQL中information_schema的使用

    information_schema是MySQL中的一个虚拟数据库,用于提供关于 MySQL 服务器及其数据库的元数,这些元数据包括数据库名称、表名称、列的数据类型、访问权限等信息,下面就来详细的介绍一下如何使用,感兴趣的可以了解一下
    2025-08-08
  • mysql创建的外键无法保存的原因以及处理办法

    mysql创建的外键无法保存的原因以及处理办法

    这篇文章主要介绍了mysql创建的外键无法保存的原因以及处理办法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • CentOS6.4上使用yum安装mysql

    CentOS6.4上使用yum安装mysql

    这篇文章主要为大家详细介绍了CentOS6.4上使用yum安装mysql图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • MySQL业务数据量增长到单表成为瓶颈时的解决方案

    MySQL业务数据量增长到单表成为瓶颈时的解决方案

    文章详细介绍了MySQL在单表数据量增长到瓶颈时的解决方案,包括应急与优化、架构升级和终极解决方案,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • MySQL中UNION与UNION ALL的基本使用方法

    MySQL中UNION与UNION ALL的基本使用方法

    这篇文章主要给大家介绍了关于MySQL中UNION与UNION ALL的基本使用方法,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-12-12
  • MySQL超详细实现用户管理实例

    MySQL超详细实现用户管理实例

    MySQL 是一个多用户数据库,具有功能强大的访问控制系统,可以为不同用户指定不同权限。在前面的章节中我们使用的是 root 用户,该用户是超级管理员,拥有所有权限,包括创建用户、删除用户和修改用户密码等管理权限
    2022-06-06

最新评论