
DISTINCT 是 SQL 中最常用也最容易被误用的关键词之一。它看似简单——去重,但实际执行时可能引发全表扫描、临时表膨胀、排序开销剧增等问题,尤其在大数据量、多字段、高并发场景下,性能骤降很常见。优化 DISTINCT 的核心不是“怎么写”,而是“为什么需要去重”以及“能否换种方式避免去重”。
先问一句:真的需要 DISTINCT 吗?
很多 DISTINCT 是“防御性写法”:怕 JOIN 多对一导致重复,就无脑加 DISTINCT。这往往掩盖了真正的数据模型或关联逻辑问题。
- 检查 JOIN 条件是否准确——比如用 LEFT JOIN 关联用户表和订单表,却没加 ON u.id = o.user_id AND o.status = 'paid',导致一个用户多笔有效订单被重复计数,再用 DISTINCT 掩盖,结果是查得慢、结果还可能错(比如漏掉某条本该保留的记录)
- 确认业务语义——要的是“有多少个不同城市”,还是“每个城市的最新一条记录”?前者用 DISTINCT city 合理;后者用 DISTINCT city 就会丢失时间、ID 等关键信息,应该改用窗口函数或 GROUP BY + 聚合
- 用 EXPLAIN 查看执行计划:如果出现 Using temporary; Using filesort,说明 MySQL 正在内存/磁盘建临时表排序去重,这是性能红灯
能替代 DISTINCT 的更高效写法
多数时候,DISTINCT 可被语义等价但执行更轻量的方式替代:
- 用 EXISTS 替代 IN + DISTINCT:例如“查所有有订单的用户”写成 SELECT DISTINCT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id,可改为 SELECT u.id, u.name FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id) ——避免生成中间笛卡尔积,通常快 2–5 倍
- 用 GROUP BY 替代多字段 DISTINCT:当 SELECT 字段全是索引覆盖列时,MySQL 5.7+ 对 GROUP BY 有优化(松散索引扫描),比 DISTINCT 更快;且便于后续扩展聚合逻辑(如加 COUNT(*))
- 用 ROW_NUMBER() 或 LATERAL(PostgreSQL / MySQL 8.0+)取每组首行:比如“每个部门薪资最高的员工”,不要写 SELECT DISTINCT dept, MAX(salary) ... GROUP BY dept 再二次关联,而应直接用窗口函数定位唯一行
必须用 DISTINCT 时的实操优化点
如果绕不开,就从执行路径上压榨效率:
- 确保 SELECT 的字段尽量少,且优先选有索引的列:DISTINCT 的去重键越短、越有序,哈希/排序越快。避免 SELECT DISTINCT * 或包含 TEXT/BLOB 字段
- 在 WHERE 中尽早过滤,再 DISTINCT:把时间范围、状态条件写在前面,而不是先 DISTINCT 全量再 WHERE,否则去重成本翻倍
- 为 DISTINCT 字段建联合索引(注意顺序):例如 SELECT DISTINCT category, status FROM products,适合建索引 INDEX(category, status);若常带 WHERE category = ?,则把 category 放前更高效
- 大表分页 DISTINCT 场景慎用 LIMIT + OFFSET:SELECT DISTINCT x FROM t ORDER BY x LIMIT 10 OFFSET 10000,可能需扫描上万行才凑够 10 个不重复值。改用游标分页(WHERE x > last_seen_value LIMIT 10)更稳
不同数据库的小差异要注意
DISTINCT 行为和优化能力因引擎而异:
- MySQL:5.7 开始对单字段 DISTINCT + ORDER BY 索引列有松散索引扫描优化;但多字段 DISTINCT 仍依赖临时表;8.0 支持 CTE 和窗口函数,多数 DISTINCT 场景可重构
- PostgreSQL:DISTINCT 默认走 HashAggregate(内存哈希去重),比排序快;配合 ENABLE_HASHAGG=off 可强制走 SortAggregate 测试对比;对 NULL 值处理更严格(NULLs are equal)
- ClickHouse:DISTINCT 底层用 Set 数据结构,性能极好,但内存消耗明显;超大数据量建议用 GROUP BY + any() 聚合替代










