SQL DISTINCT去重原理_去重查询性能分析

1次阅读

DISTINCT 是对 SELECT 后所有列值组合进行整行去重,NULL 视为相同,执行依赖排序或哈希,开销随数据量和列数增加而上升;需保留特定记录时宜用窗口函数,大数据量无索引时应建复合索引。

SQL DISTINCT 去重原理_去重查询性能分析

DISTINCT 是对 SELECT 后所有列的值组合进行整体去重,不是单字段独立去重。数据库引擎拿到结果集后,会通过排序或哈希方式识别完全相同的行,只保留其中一条。NULL 值全部视为相同,最终只返回一个 NULL。

去重本质是“整行比对”

只要 SELECT 列表中各字段的值完全一致(包括 NULL),就判定为重复行。例如:

  • SELECT DISTINCT name, city FROM students; —— 只要 name 和 city 两个值同时相同,就算重复
  • 不能写成 SELECT name, DISTINCT city,DISTINCT 不作用于单个字段
  • 即使 id 不同,只要 name+city 组合重复,也只留一行

DISTINCT 的执行开销在哪

多数数据库(如 MySQL)内部会先对结果集做排序或构建哈希表,再逐行比对。这意味着:

  • 数据量越大,内存和 CPU 消耗越明显
  • 多列去重时,性能随列数增加明显下降(组合值更难压缩)
  • 无索引字段参与去重时,常触发全表扫描 + 临时排序,速度骤降

什么时候该换其他方法

单纯为了去重,DISTINCT 简洁直观;但遇到以下情况,建议切换:

  • 需要保留某条特定记录(如最新、最高分)→ 用 ROW_NUMBER() OVER (PARTITION BY …… ORDER BY ……)
  • 顺带要统计数量、求平均值等 → 直接用 GROUP BY 更自然且通常更快
  • 数据量超 10 万行且字段无索引 → 加复合索引(如 INDEX(name, city))可提升 40%–60% 速度

实际优化小技巧

不改逻辑也能提效:

  • 避免 SELECT *,只选真正需要去重的列
  • 提前用 WHERE 过滤掉大量无关数据,减少去重基数
  • 检查字段基数(COUNT(DISTINCT col) 占总行比),低基数列单独去重意义不大
  • MySQL 8.0+ 环境下,若需稳定控制保留哪条,窗口函数比 DISTINCT + 子查询更可靠
text=ZqhQzanResources