当前位置: 首页
数据库
MySQL查询去重用UNION还是DISTINCT

MySQL查询去重用UNION还是DISTINCT

热心网友 时间:2026-07-24
转载

MySQL去重时,DISTINCT用于单条查询内部去重,UNION用于合并多条查询并自动去重。UNION自带排序和去重,性能较差,推荐使用UNIONALL加外层DISTINCT替代,避免隐性排序开销。单条查询内去重应直接使用DISTINCT,杜绝滥用UNION。

前言

在日常开发中,数据去重是数据库操作中常见的需求,许多开发者常纠结于两种方案的选择:

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

  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,性能更好,这也是线上检索场景的标准写法。

来源:https://www.jb51.net/database/367967wkl.htm

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
自增主键值从何而来?深入理解原理,告别只会auto_increment

自增主键值从何而来?深入理解原理,告别只会auto_increment

KingbaseES推荐使用serial、bigserial、显式sequence或identity列实现自增主键。serial创建integer并关联序列,bigserial对应bigint;显式sequence可自定义起始值等参数;identity有generatedbydefault(允许指定值)与always(禁止)两种模式。

时间:2026-07-25 22:22
Linux下瀚高数据库授权文件过期及替换解决方案

Linux下瀚高数据库授权文件过期及替换解决方案

在银河麒麟系统下,瀚高数据库hgdb-4 5试用授权20天到期后需替换正式授权文件。正确操作:停止服务,备份旧文件,将授权文件复制到 opt highgo hgdb-4 5 etc lic 并命名为hgdb lic,设置权限600和属主highgo:highgo,再启动服务。禁止直接修改data目录下的license info文件。

时间:2026-07-25 22:22
Oracle BLOB实时同步的5大技术挑战与难点解析

Oracle BLOB实时同步的5大技术挑战与难点解析

OracleBLOB实时同步面临分片组装、多列隔离、长事务跨窗口、事务回滚及大对象资源控制等技术挑战,必须在日志中精确还原完整字段值,才能保证源端与目标端数据完全一致,这对同步系统的稳健性提出了高要求。

时间:2026-07-25 22:22
MySQL禁用redo日志导致全备失败

MySQL禁用redo日志导致全备失败

MySQL全量备份失败是由于数据定义语言操作触发排序索引构建,禁用重做日志导致XtraBackup无法获取一致性备份。测试验证表明,优化表语句即使无数据也会触发该问题。根本原因在于排序索引构建过程跳过了重做日志记录,破坏了备份的一致性。

时间:2026-07-25 20:35
Kafka架构图优化与改进的全面详细步骤与实践指南

Kafka架构图优化与改进的全面详细步骤与实践指南

Kafka作为实时数据流处理的核心中间件,其底层架构虽已相当成熟,但在实际生产环境中,要充分发挥其性能潜力,仍需落实到具体的调优与架构改造上。核心目标可归纳为三点:如何承载更高的吞吐量、如何保障数据不丢失、以及故障发生时如何快速恢复。本文将从这几个关键方向出发,深入探讨如何真正榨干Kafka集群的性

时间:2026-07-25 20:35
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜