索引的排序和分组

先把前提说清楚:

表有两个索引:

  • 联合索引:idx_abcd (a, b, c)
  • 单列索引:idx_c (c)

你问的两条 SQL:

  1. WHERE b = 1 ORDER BY c不走索引,全表扫描 + filesort
  2. WHERE b = 1 GROUP BY c能用上 idx_c(c 的单列索引)

下面讲透为什么不一样(完全从优化器成本 + 索引结构角度讲)。


一、先说:两条 SQL 都用不了联合索引 (a,b,c)

联合索引要满足最左前缀 :必须从 a 开始,连续向右匹配。

  • 你的条件只有 b=1没有 a
  • 所以:(a,b,c) 直接废掉,两条语句都用不了它

现在只剩一个候选索引:idx_c (c)


二、为什么 WHERE b=1 ORDER BY c 不用 idx_c

语句:

sql 复制代码
WHERE b = 1 ORDER BY c

1. 索引 idx_c 是按 c 排序的,不是按 b

索引结构大概是:

复制代码
c=1 → 行地址
c=2 → 行地址
c=3 → 行地址
...

不按 b 分组/排序

2. 用 idx_c 的执行过程会是:

  • 遍历整个 c 索引(所有 c 值)
  • 每一行都要回表去判断:b 是不是等于 1
  • 筛选完后,结果集还要再按 c 排序(本来 c 有序,但中间被 b 过滤打断,不是连续的)

→ 优化器一算:回表 + 过滤 + 还要排序,成本比全表扫描还高

→ 干脆:放弃索引,直接全表扫描,然后 filesort

一句话:ORDER BY c 需要 c 整体连续有序,但 WHERE b=1 会把 c 的连续顺序"切碎",用 c 索引得不偿失。


三、为什么 WHERE b=1 GROUP BY c 反而能用 idx_c

语句:

sql 复制代码
WHERE b = 1 GROUP BY c

1. GROUP BY 的本质:按 c 去重 + 分组

只要数据按 c 有序,就能:

  • 顺序扫 c 索引
  • 遇到新 c → 开新组
  • 同 c 连续出现 → 合并到同一组

不需要严格"所有行都满足 b=1"再分组,可以边扫边过滤、边分组。

2. 用 idx_c 的执行逻辑(松散/紧凑索引扫描)

  • c 从小到大扫 idx_c
  • 每一行:回表看 b=1
    • 是 → 计入当前 c 组
    • 否 → 跳过
  • c 变了 → 输出上一组,开新组

优点:

  • 天然按 c 有序,不需要 filesort
  • 分组逻辑和索引顺序完美匹配
  • 优化器认为:比全表扫描快

所以:GROUP BY c 能利用 c 索引的有序性,即使 WHERE 有 b 过滤,依然划算。


四、一句话总结(面试直接背)

  • WHERE b=1 ORDER BY c
    c 索引有序被 b 过滤打乱,用索引要大量回表+排序,成本高 → 放弃索引,全表扫描。
  • WHERE b=1 GROUP BY c
    GROUP BY 只需要 c 有序去重分组,可以边扫边过滤,避免 filesort → 选择 c 单列索引。
相关推荐
丙氨酸長鏈15 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
独行侠影a16 小时前
APScheduler+Redis 分布式定时任务:解决多实例任务重复执行
数据库·redis·分布式
传说故事16 小时前
数据库中一些常用英文单词含义
数据库·oracle
夏贰四17 小时前
中小企业搭建业务中台如何控成本?业务中台轻量化落地分几步实施?
数据库·业务中台
林焱RPA17 小时前
影刀RPA200篇纪念版:最实用的20个自动化场景大盘点
数据库
名字还没想好☜17 小时前
Go 的 time.After 在 select 循环里内存泄漏:定时器堆积原理与 timer.Reset 正确姿势
java·数据库·golang·go·goroutine
每天都要进步哦19 小时前
SQL单行函数详解:字符、数字、日期、转换与通用函数全解析
数据库·mysql
不剪发的Tony老师19 小时前
VeloxDB:一款免费开源的轻量级数据库管理工具
数据库·sql
dotnet9019 小时前
批量生成所有表的修改脚本
数据库·sql·oracle
@Mike@19 小时前
06-数据库学习笔记(存储模型与数据压缩)
数据库·笔记·学习