索引的排序和分组

先把前提说清楚:

表有两个索引:

  • 联合索引: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 单列索引。
相关推荐
爱莉希雅&&&1 小时前
zabbix快速搭建和使用
android·linux·数据库·zabbix·监控
不爱编程的小陈1 小时前
事务的进化:从MySQL单机事务到TiDB分布式事务的探究
分布式·mysql·tidb
JohnYan1 小时前
工作笔记 - PG分组极值
数据库·后端·postgresql
清溪5491 小时前
DataEase H2 JDBC-RCE(CVE-2025-32966)复现
数据库·安全
ServBay2 小时前
不要再盲选了,PostgreSQL、MySQL与SQLite真实性能对比
数据库·mysql·sqlite
Trouvaille ~2 小时前
【Redis篇】Set 与 Zset:集合运算与排行榜的终极武器
数据库·redis·缓存·set·跳表·后端开发·zset
無限進步D2 小时前
MySQL 创建和管理表
数据库·mysql
六月雨滴2 小时前
归档模式配置与切换
数据库·oracle·dba
卡次卡次12 小时前
vibecoding起步注意点:插件、Skills、MCP、Hooks
服务器·数据库·python·oracle