【SQL】DISTINCT 与 GROUP BY 核心区别及常见误区、问题全梳理

一、核心区别(从本质到用法,结合业务场景)

维度 DISTINCT GROUP BY
设计初衷 去除查询结果中整行重复的记录,仅保留唯一行(纯去重工具) 对数据按指定字段分组,为聚合统计(SUM/COUNT/MAX)服务(统计工具)
去重粒度 整行去重:只有所有查询字段值完全相同,才判定为重复 单字段 / 多字段分组去重:仅按分组字段去重,非分组字段需聚合 / 规则取值
是否支持聚合 仅支持 COUNT(DISTINCT 字段)(统计单个字段唯一值数量),无其他聚合能力 核心能力是分组聚合,可搭配 SUM/MAX/COUNT 等所有聚合函数
性能特点 轻量高效:MySQL 对整行去重有哈希 / 排序优化,适合简单去重 相对较重:分组需排序 / 分组运算,纯去重场景性能低于 DISTINCT
语法要求 必须放在查询字段最前,无额外约束(仅整行去重) MySQL 开启 only_full_group_by 时,非分组字段必须聚合;关闭则随机取值
业务适配场景 筛选 "唯一字段组合"(如唯一的「账单号 + 金额」),无需统计 按核心维度统计(如按账单号算总收款、按项目编号算资产数)

二、最常见的 4 个误区(结合业务沟通痛点)

误区 1:认为 DISTINCT 能实现 "单字段去重 + 保留其他字段"
  • 错误认知 :业务提 "账单号去重,保留金额 / 标题",开发想通过 DISTINCT billNumber, amount 实现 "仅账单号唯一"。
  • 实际效果:DISTINCT 是整行去重,若同一账单号对应不同金额(如 B001 有 500/1000 两笔),仍会返回多条记录,完全达不到业务 "一个账单号只显示一条" 的需求。
  • 核心矛盾:业务要 "单字段唯一",DISTINCT 只能 "整行唯一",两者逻辑不匹配,这也是业务觉得 "去重没效果" 的核心原因。
误区 2:用 GROUP BY 纯去重(无聚合),把分组当 "去重工具"
  • 错误认知 :开发为满足业务 "去重" 需求,直接写 SELECT billNumber, amount FROM 表 GROUP BY billNumber,忽视 GROUP BY 的聚合初衷。
  • 实际后果
    • 开启 only_full_group_by:直接报错(非分组字段未聚合);
    • 关闭 only_full_group_by:MySQL 随机取分组内某一行的金额,数据可能失真(如财务对账时金额对不上);
  • 业务视角:仅看到 "账单号不重复",短期内不会发现金额错误,埋下后期追责风险。
误区 3:把 "多表关联膨胀" 当成 "数据重复",盲目去重
  • 错误认知:开发看到多表关联后出现重复行(如 1 条账单关联 3 条收款记录),直接用 DISTINCT/GROUP BY 去重,认为是 "数据重复" 问题。
  • 实际根源:重复行是 "一对多关联" 导致的(主表 1 行 → 子表 N 行),而非数据本身重复;单纯去重会丢失子表核心信息(如收款总额)。
  • 典型场景:账单表关联收款表后,GROUP BY 去重账单号,导致收款金额只显示单笔(而非总和),财务对账时才发现数据缺失。
误区 4:为应付业务需求,关闭 only_full_group_by 规避报错
  • 错误认知 :开发嫌聚合 / 开窗函数麻烦,直接修改数据库配置关闭 only_full_group_by,任由 MySQL 随机选非分组字段值。
  • 隐藏风险
    • 数据随机:同一查询多次执行可能返回不同结果(如 B001 金额忽为 500 忽为 1000);
    • 迁移坑:新版本 MySQL(8.0+)默认开启该规则,后期迁移 / 升级会批量报错;
    • 合规风险:审计 / 财务场景下,"随机取值" 无法追溯,直接违反数据合规要求。

三、使用中高频踩坑的 5 个问题(结合业务落地痛点)

问题 1:去重后子表数据缺失 / 失真,业务未细查难发现
  • 表现:账单号去重后,收款金额只取单笔(而非总和)、开票状态取到旧值,业务仅看列表 "不重复" 不会发现,对账 / 审计时才暴露。
  • 根源:开发未先聚合子表(如 SUM 收款金额、MAX 最新状态),直接去重导致子表信息丢失。
  • 解决方案 :先聚合子表(如 SUM(sub.collectionAmount)),再关联主表,既保证核心字段唯一,又保留子表完整信息。
问题 2:业务 "不专业" 导致需求模糊,开发被动埋雷
  • 表现:业务仅提 "去重,一个账单号只显示一条",未提 "要总金额 / 最新状态",开发用 GROUP BY 随机取值应付,后期数据错误追责开发。
  • 沟通技巧:不用讲技术术语,只需说 "我把重复账单号合并成一条,顺便加了总收款金额 / 最新开票状态,你看是否合适",悄悄兜底数据准确性。
问题 3:DISTINCT 与 ORDER BY 冲突,排序失效
  • 表现 :用 DISTINCT 去重后,排序字段不在查询字段中,导致排序失效或报错。
  • 解决方案 :将排序字段加入 DISTINCT 的查询字段中(如 DISTINCT billNumber, createTime ORDER BY createTime)。
问题 4:过度依赖去重,忽视数据治理根源
  • 表现:反复用 DISTINCT/GROUP BY 处理重复行,却不解决数据录入不规范(如账单号重复录入、子表关联条件不唯一)。
  • 后果:去重成为 "常态操作",数据失真风险持续存在,后期排查问题成本极高。
  • 根本解法:核心字段加唯一索引(如账单号)、规范录入流程,从源头避免重复数据。
问题 5:主动聚合被认为 "多此一举",开发陷入两难
  • 表现:开发主动做聚合(如 SUM 收款金额)满足数据准确性,但业务未提该需求,觉得 "列表字段太多 / 没必要"。
  • 折中方案 :用 ROW_NUMBER() 按规则取行(如 rn=1 取每组第一条),既满足 "去重" 需求,又保证数据稳定(非随机),无需额外聚合字段。

四、正确用法与避坑指南(结合工程落地)

表格

业务需求 正确技术方案 绝对禁止的做法
单字段去重 + 保留其他字段 ROW_NUMBER() OVER(PARTITION BY 核心字段 ORDER BY 主键) 取每组第一条 关闭 only_full_group_by 后用 GROUP BY 随机取值
整行去重 直接用 DISTINCT(如 DISTINCT billNumber, amount, status 用 GROUP BY 模拟整行去重(性能更低)
分组统计(求和 / 计数) GROUP BY + 聚合函数(如 GROUP BY billNumber SUM(collectionAmount) DISTINCT 后再手动统计(易出错)
临时内部报表(非核心) 可临时用 ANY_VALUE(非分组字段)(如 ANY_VALUE(amount)),无需聚合 关闭 only_full_group_by 随机取值
财务 / 核心业务 先聚合子表,再关联主表(保证数据精准) 任何形式的随机取值、盲目去重

五、核心结论

  1. 本质定位:DISTINCT 是 "整行去重工具",GROUP BY 是 "分组聚合工具",两者都不是 "单字段去重 + 保留其他字段" 的合理方案;
  2. 业务适配:业务提的 "去重",本质是 "将一对多数据整理为一对一",正确解法是 "先聚合子表,再关联主表" 或 "按规则取行(ROW_NUMBER)",而非单纯依赖 DISTINCT/GROUP BY;
  3. 风险底线 :核心业务(财务 / 订单 / 收款)绝对不能关闭 only_full_group_by,也不能用 GROUP BY 随机取值,数据稳定比 "省事" 更重要;
  4. 沟通原则:业务不懂技术逻辑,但开发需兜底数据准确性 ------ 不用硬怼需求,而是用 "规则化取行 / 轻量聚合" 悄悄规避风险。

简单来说:DISTINCT 管 "整行唯一",GROUP BY 管 "分组统计",业务要的 "去重" 往往是 "整理一对多数据",而非真的 "删重复行";开发的核心责任是在满足业务 "表面需求" 的同时,守住数据准确的底线。

相关推荐
曾阿伦2 小时前
SQL 用法详解:从基础操作到进阶实战的全场景指南
数据库·sql
阿里云大数据AI技术2 小时前
EMR Serverless Spark 携手 PAI/百炼,开启“SQL 即 AI”的新篇章
sql·阿里云·spark·serverless·pai
Meepo_haha2 小时前
python的sql解析库-sqlparse
数据库·python·sql
Seven972 小时前
揭秘MySQL索引分类
mysql
常利兵2 小时前
Java后端定时任务抉择:@Scheduled、Quartz、XXL - Job终极对决
java·数据库·sql
me8322 小时前
【Java】关于控制台 SQL 日志显示查询有值但Swagger不显示字段问题
java·开发语言·sql
AI算法董同学2 小时前
【MySQL】项目后端 MySQL 数据库初始化步骤
数据库·mysql·adb
云边有个稻草人2 小时前
【MySQL】第十三节—索引:底层原理、B + 树演进、操作实战
mysql·索引·聚簇索引·主键索引·唯一键索引·普通索引·创建索引
信也科技布道师2 小时前
信也AI赋能慢SQL治理的探索与实践
数据库·人工智能·sql