MySQL查询去重是使用UNION还是使用DISTINCT 标签MySQL、SQL优化、去重、UNION、DISTINCT、UNION ALL、执行计划前言开发过程中经常面临数据去重需求大家常会纠结两种方案使用DISTINCT在单条结果集中完成去重使用UNION合并多条查询并自动去重。还有很多开发者分不清UNION和UNION ALL的巨大差异经常误用导致数据库出现不必要的性能消耗。本文对比两者原理、适用场景、性能差距给出线上环境选型标准。一、先理清基础语法与核心行为1. DISTINCT作用对单条SQL的结果集进行去重。SELECTDISTINCTuser_idFROMuser_login_logWHEREdateCURDATE();执行逻辑数据库取出所有满足条件的数据按照指定字段进行分组对比剔除重复行保留唯一记录。2. UNION 与 UNION ALL重点区分-- UNION合并结果 自动去重 排序SELECTuser_idFROMuserWHEREstatus1UNIONSELECTuser_idFROMapp_keyWHEREstatus1;-- UNION ALL仅简单纵向拼接**不去重、不排序**SELECTuser_idFROMuserWHEREstatus1UNIONALLSELECTuser_idFROMapp_keyWHEREstatus1;很多人踩坑以为 UNION UNION ALL二者性能差距极大。UNION UNION ALL DISTINCT 排序操作二、底层实现原理对比DISTINCT 原理在结果集内部构建临时内存哈希表或者排序缓冲区遍历数据消除本行内重复记录。数据量较小使用内存数据量大超过缓冲区限制则落地磁盘临时文件性能断崖下跌。UNION 原理分别执行前后两条子查询使用UNION ALL把所有数据纵向汇总全局执行一次DISTINCT排序去重。简单公式UNION UNION ALL DISTINCT三、核心性能结论如果业务需要合并多条SQL结果并且去重可以使用UNION如果多条SQL合并原始数据不存在重复优先使用UNION ALL不要用 UNION如果只是单表/单条查询内部去重不要使用 UNION直接使用DISTINCT杜绝滥用 UNION 实现单条SQL内部去重属于完全错误用法。四、场景分类实战分析场景1单条查询内部去除重复数据 ✅ DISTINCT需求查询当日登录日志里所有活跃用户ID同一用户多条登录记录只展示一次。-- 正确写法SELECTDISTINCTuser_idFROMuser_login_logWHEREdateCURDATE();-- ❌ 错误示范没必要强行拆分UNIONSELECTuser_idFROMuser_login_logWHEREdateCURDATE()UNIONSELECTuser_idFROMuser_login_logWHEREdateCURDATE();强行使用UNION会执行两次相同查询扫描双倍数据额外执行全局去重资源翻倍浪费。场景2多条独立查询结果合并需要全局去重需求从用户表、密钥表两处查询user_id合并结果同一个user_id只保留一条。方案A UNIONSELECTuser_idFROMuserWHEREusernamedemoUNIONSELECTuser_idFROMapp_keyWHEREaccess_keydemo_key;方案B UNION ALL 外层DISTINCTSELECTDISTINCTuser_idFROM(SELECTuser_idFROMuserWHEREusernamedemoUNIONALLSELECTuser_idFROMapp_keyWHEREaccess_keydemo_key)t;重点方案A 和方案B哪个更快绝大多数情况下UNION ALL 外层DISTINCT 性能 ≥ UNION原因UNION默认会附带排序行为而外层DISTINCT优化器可以选择哈希去重不一定强制排序优化空间更大。追求稳定高性能推荐统一使用UNION ALL DISTINCT写法避免UNION隐性排序带来开销。场景3多条查询合并明确不存在重复数据✅ 直接使用UNION ALL不要使用UNION不要额外加DISTINCT省去全局比较、排序、去重的巨大开销。SELECTidFROMuserLIMIT100UNIONALLSELECTidFROMapp_keyLIMIT100;五、高频误区汇总误区1UNION 和 DISTINCT 可以随意互相替换❌ 不能替换。DISTINCT作用于单查询内部UNION作用于多条查询合并之后适用场景边界完全不同。误区2UNION去重性能优于 UNION ALL DISTINCT❌ 恰恰相反。UNION强制执行排序去重UNION ALL只做拼接把去重选择权交给外层优化器拥有更多优化策略。误区3少量数据随便写无所谓在测试环境少量数据看不出差距当结果集上万、十万级别UNION额外排序会直接引发慢查询线上极易爆出性能故障。误区4不知道UNION自带排序导致不必要的消耗MySQL UNION规范合并完成后会执行排序操作如果你不需要排序不要使用UNION。六、索引层面额外优化提示DISTINCT查询尽量建立覆盖索引避免大量回表-- 示例利用索引直接完成去重无需读取原始数据表CREATEINDEXidx_date_userONuser_login_log(date,user_id);UNION ALL拆分多条查询时每条子查询务必保证可以正常命中索引如果最终只需要获取第一条匹配记录可以每层子查询增加 LIMIT 实现短路查询减少扫描行数。七、选型决策清单线上直接套用仅单条SQL内部去重→ 使用DISTINCT多条SQL结果合并存在重复且需要去重→ 优先UNION ALL 外层DISTINCT多条SQL结果合并确认无重复→ 使用UNION ALL禁止单条查询场景强行拆分使用UNION做去重禁止能用UNION ALL的场景随意使用UNION八、验证手段使用EXPLAIN观察执行计划UNION通常能看到Using temporary; Using filesort临时表文件排序UNION ALL没有全局排序与临时表执行计划更加简洁总结一句话去重工具没有绝对好坏分清场景再选择能使用 UNION ALL 就不要使用 UNION能避免全局排序就尽量避免。延伸业务小案例你项目常用场景根据账号、邮箱、密钥多条件检索用户ID-- 最优写法SELECTDISTINCTuser_idFROM(SELECTuser_idFROMuserWHEREusernamedemoUNIONALLSELECTuser_idFROMuserWHEREemaildemotest.comUNIONALLSELECTuser_idFROMapp_keyWHEREaccess_keydemo_key)tmp;相比直接写三条UNION性能更好也是线上检索场景标准写法。