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 是纯粹的去重工具,直接丢弃重复行,语法更简洁。
  • 性能关键:合理使用索引(覆盖索引、唯一索引)可大幅提升两者效率。
相关推荐
M1A118 小时前
小红书重磅升级!公众号文章一键导入,深度内容轻松入驻
后端
0wioiw019 小时前
Go基础(④指针)
开发语言·后端·golang
李姆斯20 小时前
复盘上瘾症:到底什么时候该“复盘”,什么时候不需要“复盘”
前端·后端·团队管理
javachen__21 小时前
Spring Boot配置error日志发送至企业微信
spring boot·后端·企业微信
seabirdssss21 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
OC溥哥9991 天前
Flask论坛与个人中心页面开发教程完整详细版
后端·python·flask·html
迷知悟道1 天前
java面向对象四大核心特征之抽象---超详细(保姆级)
java·后端
Aurora_NeAr1 天前
对比Java学习Go——程序结构与变量
后端
AntBlack1 天前
每周学点 AI:ComfyUI + Modal 的一键部署脚本
人工智能·后端·aigc
5大大大大雄1 天前
docker容器日志处理
后端