为什么SQL中使用DISTINCT后会导致查询性能急剧下降怎么破?

风宇吖_2256

风宇吖_2256

2026-07-05

889人浏览

原创

distinct 本质是隐式 group by,需全量拉取数据建哈希表或排序去重,易引发临时表膨胀、磁盘 io 增加及谓词下推失效;仅当 select 字段完全匹配联合索引最左前缀且 where 条件命中该索引时才可能走索引。

为什么sql中使用distinct后会导致查询性能急剧下降怎么破?

为什么DISTINCT会让查询变慢

DISTINCT 不是过滤器,它本质是隐式 GROUP BY:数据库必须把所有 SELECT 字段的值全拉出来,建哈希表或排序去重。中间结果集可能比原表还大,尤其当字段含 TEXT、JSON 或多列组合时,内存和临时磁盘压力陡增。

常见错误现象:EXPLAIN 里出现 Using temporary 和 Using filesort;响应时间从毫秒跳到秒级;tmp_table_size 被撑爆后写磁盘临时表。

  • MySQL 8.0+ 对单表 DISTINCT 启用哈希聚合,但一旦混用 JOIN、GROUP BY 或子查询,立刻退化回排序路径
  • PostgreSQL 默认走排序,不自动哈希;SQLite 只对单列 DISTINCT 有索引短路优化,多列无效
  • DISTINCT 会关闭谓词下推——子查询里加了它,外层 WHERE 就没法下推,哪怕只取 LIMIT 10,也得先把 50 万行全算出来

怎么让 DISTINCT 走索引

能走索引的前提很具体:必须是 SELECT 的所有字段,都出现在同一个联合索引的最左前缀中,且 WHERE 条件也命中该索引。

例如表 orders(user_id, status, created_at),执行 SELECT DISTINCT user_id, status FROM orders 可走索引;但 SELECT DISTINCT status, user_id 就不行(顺序不匹配)。

Pic Copilot
Pic Copilot

Pic Copilot是阿里国际推出的面向电商卖家的 AI 商品图和营销设计工具。

下载
  • 如果加了 WHERE created_at > '2024-01-01',而索引是 (user_id, status),那依然用不上——created_at 不在索引里
  • 正确做法是建覆盖索引:CREATE INDEX idx_created_user_status ON orders(created_at, user_id, status)
  • 别指望 ORDER BY 和 DISTINCT 共享索引优化:MySQL 5.7 不支持;8.0+ 仅当 ORDER BY 字段完全被 DISTINCT 字段包含时才可能复用

用 GROUP BY 替代 DISTINCT 真的更高效吗

语义等价时,GROUP BY 常比 DISTINCT 更可控——尤其配合索引时,某些引擎(如 ClickHouse)对 GROUP BY 的优化更成熟。

比如 SELECT DISTINCT city, category FROM t,可改写为 SELECT city, category FROM t GROUP BY city, category,再配上索引 (city, category),效果一致但执行计划更稳定。

  • GROUP BY 支持后续加聚合函数(如 MIN(updated_at)),DISTINCT 不能
  • 注意 MySQL 的 ONLY_FULL_GROUP_BY 模式:若 SELECT 列没全在 GROUP BY 中,会报错,不是性能问题而是语义校验
  • 别写 SELECT DISTINCT dept_id, COUNT(*) FROM t GROUP BY dept_id——这是逻辑冲突:GROUP BY 已保证 dept_id 唯一,DISTINCT 纯属多余,却强制扫两次表

真正该先问的问题:你真的需要 DISTINCT 吗

很多 DISTINCT 是“补丁式写法”:JOIN 导致笛卡尔积,再靠它兜底。这掩盖了数据模型缺陷,代价远高于修复本身。

先检查 JOIN 条件是否完整、关联字段是否有索引;用 EXISTS 替代 JOIN + DISTINCT 查存在性;把去重下沉到 CTE 或子查询阶段,而不是最后一步硬扛。

  • 查“买过商品 A 的用户”,别写 SELECT DISTINCT u.id FROM users u JOIN orders o ON u.id = o.user_id JOIN items i ON o.item_id = i.id WHERE i.name = 'A'
  • 改用 SELECT u.id FROM users u WHERE EXISTS (SELECT 1 FROM orders o JOIN items i ON o.item_id = i.id WHERE o.user_id = u.id AND i.name = 'A')
  • 如果只是要统计数,COUNT(DISTINCT) 在亿级数据上根本不是调优问题——是架构选择问题:接受 ±1% 误差就用 APPROX_COUNT_DISTINCT,否则必须预聚合
真实瓶颈往往不在语法怎么写,而在要不要在实时查询里做精确去重。高基数字段、多字段组合、嵌套视图里的 DISTINCT,几乎都会触发临时表膨胀或全量物化——这时候索引再好也没用。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

3943

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

851

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1029

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5801

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2723

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5780

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7661

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1050

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

912

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL索引优化解决方案
MySQL索引优化解决方案

共23课时 | 2.8万人学习