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 是纯粹的去重工具,直接丢弃重复行,语法更简洁。
  • 性能关键:合理使用索引(覆盖索引、唯一索引)可大幅提升两者效率。
相关推荐
WanderInk4 小时前
深入解析:Java Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); 一行代码的背后功力
java·后端·算法
codeGoogle4 小时前
“ASIC项目90%会失败”,黄仁勋的“诅咒”劝退华为?
后端
追逐时光者4 小时前
一款基于 .NET 开源免费、轻量快速、跨平台的 PDF 阅读器
后端·.net
默默地离开5 小时前
前端开发中的 Mock 实践与接口联调技巧
前端·后端·设计模式
杨荧6 小时前
基于爬虫技术的电影数据可视化系统 Python+Django+Vue.js
开发语言·前端·vue.js·后端·爬虫·python·信息可视化
在雨季等你7 小时前
奋斗在创业路上的老开发
android·前端·后端
转转技术团队7 小时前
游戏账号大图生成
java·后端
程序员爱钓鱼7 小时前
Go语言实战案例-批量重命名文件
后端·google·go
大熊计算机7 小时前
大模型推理加速实战,vLLM 部署 Llama3 的量化与批处理优化指南
后端
程序员爱钓鱼7 小时前
Go语言实战案例-遍历目录下所有文件
后端·google·go