SQL 去重效率天差地别?GROUP BY 和 DISTINCT 的底层厮杀

GROUP BYDISTINCT 是 SQL 中用于去重的两种方式,但它们的核心用途、底层原理和性能表现差异显著。以下是详细对比:

1. 核心用途差异

功能 GROUP BY DISTINCT
主要目的 分组聚合(如计算每组的 SUMCOUNT 返回结果集的唯一行(去重)
聚合函数 必须搭配聚合函数(如 SUMAVG 不支持聚合函数,仅返回原始字段
示例 GROUP BY dept_id + SUM(salary) DISTINCT city

2. 底层执行原理

GROUP BY 的执行流程

  1. 分组阶段
    • 按照 GROUP BY 指定的字段对结果集进行排序(如果未使用索引)。
    • 将相同值的行放入同一个组中。
  2. 聚合阶段
    • 对每个组应用聚合函数(如 SUMCOUNT),生成单行结果。
  3. 索引优化
    • 如果 GROUP BY 字段有索引,数据库会直接利用索引的有序性快速分组(避免排序)。
    • 例如,MySQL 的 filesort 操作可被索引避免。

DISTINCT 的执行流程

  1. 排序/哈希去重
    • 排序法:对结果集按所有选择的字段排序,相邻重复行只保留一行。
    • 哈希法:构建哈希表,遍历结果集时丢弃哈希冲突的重复行。
  2. 索引优化
    • 如果 SELECT 的字段包含索引,数据库直接扫描索引(无需回表)。
    • 例如,SELECT DISTINCT email 可通过 email 索引快速去重。

3. 性能对比

场景 GROUP BY DISTINCT
无聚合的简单去重 需额外排序开销,性能较差 专为去重优化,性能更好
带聚合函数的分组 直接分组聚合,性能最优 无法实现,必须用 GROUP BY
索引优化 依赖索引避免排序(如覆盖索引) 依赖索引避免回表(如唯一索引)
大数据量去重 可能触发磁盘临时文件(排序溢出) 哈希法更高效(内存足够时)

4. 示例对比

假设有表 orders

user_id product amount
1 A 100
1 B 200
2 A 150

场景1:统计每个用户的总金额(聚合)

sql 复制代码
SELECT user_id, SUM(amount)  -- 必须用 GROUP BY
FROM orders
GROUP BY user_id;
  • 结果

    user_id SUM(amount)
    1 300
    2 150

场景2:列出所有购买过的产品(去重)

sql 复制代码
SELECT DISTINCT product;  -- 简洁写法
-- 等价于(但性能更差):
SELECT product FROM orders GROUP BY product;
  • 结果

    product
    A
    B

5. 进阶区别

  1. NULL 处理

    • DISTINCT 将所有 NULL 视为相同值(只保留一个)。
    • GROUP BY 同样将 NULL 分为一组(如果字段允许为 NULL)。
  2. 多字段组合

    • DISTINCT col1, col2 去重的是两个字段的组合值。
    • GROUP BY col1, col2 对组合字段分组,需搭配聚合函数。
  3. ORDER BY 的联用

    • DISTINCT 先去重,再按结果排序。
    • GROUP BY 先分组,聚合后按结果排序(索引可优化排序)。

6. 选择建议

需求 最佳选择 理由
简单去重(无聚合) DISTINCT 语法简洁,性能优化更好
分组聚合(如统计、平均值) GROUP BY 必须搭配聚合函数
多字段去重 + 排序 DISTINCT + 索引 利用索引避免排序
大数据量分组 GROUP BY + 索引 索引可避免 filesort

总结

  • GROUP BY 是为聚合而生,必须搭配聚合函数,底层通过排序或哈希实现分组。
  • DISTINCT 是纯粹的去重工具,直接丢弃重复行,语法更简洁。
  • 性能关键:合理使用索引(覆盖索引、唯一索引)可大幅提升两者效率。
相关推荐
卷无止境4 分钟前
Python的collections模块:那些被低估的"瑞士军刀"
后端·python
卷无止境9 分钟前
Python的方法解析顺序:一场关于继承顺序的精妙设计
后端·python
宁&沉沦25 分钟前
Chrome 扩展 Manifest 字段版本支持一览(全量)
前端·后端·编辑器
吃饱了得干活1 小时前
缓存与数据库一致性:从理论到实战
java·后端·面试
520拼好饭被践踏1 小时前
JAVA+Agent学习day22
java·开发语言·后端·学习
whi1 小时前
V 编译器 v3 ownership 模式:编译与使用指南
后端·编译器
swipe1 小时前
05|(前端转后全栈)不手写一堆 SQL,后端怎么操作数据库?MyBatis-Plus 入门
前端·后端·全栈
霸道流氓气质1 小时前
SpringBoot中使用JasperReports 报表引擎 — 介绍、原理与使用实践
java·spring boot·后端
swipe1 小时前
04|(前端转后全栈)前端状态为什么不够用?从页面数据到 MySQL 持久化
前端·后端·全栈
苏三说技术1 小时前
为什么越来越多人使用WebFlux?
后端