四时宝库

程序员的知识宝库

MySQL中WHERE和GROUP BY如何一起用上索引?很多都漏了一点!

日常写 SQL,大家都会使用 WHERE 和 GROUP BY,但真正理解它们如何一起使用索引,并不是每个人都能答得清楚。

多数教程讲的只是“最左前缀原则”,却没讲清楚字段不连续的情况会怎样影响索引命中

今天我们就系统聊一聊,并揭开这个大家经常忽略的性能陷阱


一句话概括核心原则:

WHERE 字段 + GROUP BY 字段 必须在一个联合索引中,顺序必须连续命中!中间字段断了,后面字段就白搭。


基础回顾:什么是最左前缀匹配?

假设你有这样的联合索引:

INDEX idx (a, b, c)

这个索引可以支持的查询顺序是:

  • WHERE a = ?
  • WHERE a = ? AND b = ?
  • WHERE a = ? AND b = ? AND c = ?
  • GROUP BY a
  • GROUP BY a, b
  • GROUP BY c(前缀断了)

理想示例:字段顺序连续

表结构

CREATE TABLE orders ( id INT PRIMARY KEY, user_id INT, status TINYINT, created_at DATE, INDEX idx (user_id, status, created_at) );

查询

SELECT created_at, COUNT(*) FROM orders WHERE user_id = 123 AND status = 1 GROUP BY created_at;

为什么能命中索引?

  • user_id → 第1列
  • status → 第2列
  • GROUP BY created_at → 第3列

字段连续出现且顺序匹配索引定义,满足最左前缀,MySQL 可以直接利用索引来过滤和分组。

执行计划

Extra: Using where; Using index for group-by

说明 MySQL 利用了索引完成了分组,无需临时表,也不用排序,效率非常高。


被忽略的陷阱:字段中间“断了”

查询:

SELECT created_at, COUNT(*) FROM orders WHERE user_id = 123 GROUP BY created_at;
  • 使用了 user_id(第1列)
  • 跳过了 status
  • 然后用 created_at(第3列)

出现什么问题?

MySQL 虽然可以使用 user_id 过滤数据,但无法跳过 status 去用 created_at 排序或分组。因为:

联合索引必须从最左连续命中,如果中间字段被跳过,后面字段无法使用索引!

执行计划:

Extra: Using where; Using temporary; Using filesort

性能大大下降,MySQL 需要扫描数据,排序,甚至使用临时表。


索引设计建议:围绕查询结构定索引顺序

查询结构

推荐索引顺序

WHERE user_id AND status GROUP BY created_at

INDEX(user_id, status, created_at)

WHERE user_id GROUP BY created_at

INDEX(user_id, created_at)

WHERE company_id GROUP BY dept_id

INDEX(company_id, dept_id)

小技巧:将 WHERE 最常用、区分度高的字段放前面,GROUP BY 字段跟在后面,组合成联合索引。


常见误区总结

误区

结果

WHERE 和 GROUP BY 字段分别用了不同索引

无法联合使用,GROUP BY 需排序

GROUP BY 字段在索引中,但前面字段没用上

后面的字段命不中

索引字段使用了函数,比如 DATE(created_at)

索引失效

用了 ORDER BY 与索引方向冲突

可能排序失效

结语:一句话记住这个原则

在 MySQL 中,WHERE 和 GROUP BY 想一起用上索引,字段必须“挨着站队”,中间不能漏人。


如果你看到这里,已经打通了 MySQL 索引优化的关键一环。不妨回头检查一下你线上用的 SQL,是不是也踩了“字段断裂”的坑?

欢迎点赞、收藏、转发,别忘了关注 @一起学习MySQL,我们下次再见!

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接