DISTINCT是对结果集整行去重的逻辑操作,实现方式包括基于排序(适合中小数据量)、基于哈希(高效但耗内存)和利用唯一索引(最优但依赖设计);其作用对象是SELECT列的组合值,非单列。

DISTINCT 并不是简单地“把重复行删掉”,而是数据库在查询执行过程中,对结果集进行去重处理的逻辑操作。它的实现机制取决于具体数据库系统、数据量大小、是否有索引、是否涉及排序以及是否启用哈希优化等因素。
基于排序的去重(Sort-Based DISTINCT)
这是最经典、兼容性最强的方式。数据库先将目标字段(或所有 SELECT 列)作为排序键,对中间结果进行排序,再顺序扫描,跳过与前一行完全相同的记录。
- 适合小到中等数据量,或已存在对应排序字段索引的场景
- 排序本身开销较大,内存不足时会落盘(产生临时文件),影响性能
- MySQL 5.7 及更早版本、PostgreSQL 在无合适哈希条件时常默认采用此方式
基于哈希的去重(Hash-Based DISTINCT)
数据库构建一个哈希表,以 DISTINCT 字段值为 key,首次遇到的行(或仅存 key)写入哈希表;后续遇到相同 key 直接跳过。扫描结束后,哈希表中的所有 key 即为去重结果。
- 通常比排序更快,尤其在高基数(重复率低)或大数据集上优势明显
- 需要足够内存支撑哈希表;内存不足时可能降级为磁盘哈希(如 PostgreSQL 的 hashagg),或回退到排序方案
- SQL Server 和较新版本的 PostgreSQL(≥9.6)、Oracle(配合 HASH GROUP BY)常优先启用
利用索引避免显式去重
如果 DISTINCT 字段上有唯一索引(或联合索引前导列覆盖 DISTINCT 列),且查询不包含其他非索引列或复杂表达式,优化器可能直接走索引扫描,天然跳过重复——因为索引结构本身已保证唯一性。
- 例如:
SELECT DISTINCT user_id FROM orders WHERE status = 'paid',若(status, user_id)有联合索引,且 user_id 在该索引中不重复出现,则可能免排序/哈希 - 这种“隐式去重”效率最高,但依赖索引设计和查询写法,不可强求
注意:DISTINCT 作用于整行,不是单列
很多人误以为 SELECT DISTINCT a, b FROM t 是分别对 a 和 b 去重。实际上,它是对 (a,b) 这个组合值 去重。只要任意一列不同,整行就视为不重复。
- 若想单独获取 a 的所有不同值,应写
SELECT DISTINCT a FROM t - 若需先按某列分组再取代表行(如每个部门最新一条记录),DISTINCT 无法满足,应改用窗口函数或 GROUP BY + 聚合
- 带 ORDER BY 时,DISTINCT 必须出现在排序字段子集中(标准 SQL 要求),否则报错或行为不一致










