SQL数据库Distinct实现机制_去重策略分析

13次阅读

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

SQL数据库Distinct实现机制_去重策略分析

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 要求),否则报错或行为不一致

实际执行计划中,可通过 EXPLAIN 查看是 HashAggregate 还是 GroupAggregate(PostgreSQL)、stream Aggregate 或 Hash Match(SQL Server)来判断底层机制。理解这些差异,有助于写出更可控、可优化的去重查询。

text=ZqhQzanResources