SQL查询慢的核心原因是扫描行数过多和过滤率过低;前者因全表扫描、索引范围过大或联合索引顺序错误导致,后者因索引选择性差、条件区分度低或隐式转换引起,需通过EXPLAIN和profiling定位并针对性优化索引设计与写法。

SQL报表查询慢,核心问题往往不是SQL写得“丑”,而是数据库在执行时扫描了太多行、又过滤掉大部分——也就是扫描行数过多和过滤率过低。这两者直接决定执行计划是否高效,也决定了加索引有没有效果、加了为何还不快。
为什么扫描行数多会导致慢?
扫描行数(rows_examined)代表MySQL实际读取并判断的记录条数。它不等于返回结果数(rows_sent),也不等于表总行数。即使只查10条数据,若没走对索引,也可能扫几万甚至百万行:
- 全表扫描:没有可用索引或索引失效,引擎逐行读取+判断WHERE条件
- 索引扫描但范围过大:比如用LIKE '%关键词'导致无法用索引下推,只能先扫整个索引再回表过滤
- 联合索引顺序错:WHERE中跳过了最左前缀字段(如索引是 (a,b,c),却只用了 b = ?),导致索引部分失效
过滤率低是怎么拖慢查询的?
过滤率 = 返回行数 / 扫描行数。理想值接近1(扫100行返回95行),差的情况可能只有0.001(扫10万行只返回100行)。低过滤率说明:
- 索引选择性差:比如对性别字段建索引,只有“男/女”两个值,优化器大概率弃用
- WHERE条件区分度弱:如 status IN ('待处理','处理中','已关闭') 覆盖80%数据,索引帮助有限
- 隐式类型转换或函数操作:如 WHERE DATE(create_time) = '2024-01-01',create_time索引完全失效
怎么快速定位扫描行数和过滤率?
别只看执行时间,重点看EXPLAIN和profiling结果:
- 执行 EXPLAIN FORMAT=JSON SELECT ...,关注 rows_examined 和 filtered 字段(后者就是预估过滤率)
- 开启 profiling:SET profiling = 1; 执行SQL后,用 SHOW PROFILES; 和 SHOW PROFILE FOR QUERY N; 查各阶段耗时,确认瓶颈在Sending data(即扫描过滤阶段)还是其他环节
- 用 SELECT @@last_query_cost; 看优化器估算的IO成本,数值越大越可能慢
针对性优化建议
不堆索引,要让每一条索引都“精准打击”:
- 优先覆盖高频、高区分度查询字段组合,按WHERE → ORDER BY → SELECT顺序设计联合索引
- 把等值条件放最左,范围查询(>、BETWEEN、LIKE 'abc%')放中间,排序字段放最后(避免filesort)
- 避免在WHERE里对字段做运算或函数,改用范围改写:例如 create_time >= '2024-01-01' AND create_time < '2024-01-02'
- 大表分页慎用 OFFSET:limit 10000,20 实际仍扫10020行,可改用游标分页(记录上一页最大ID)










