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

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 + 子查询更可靠






























