MySQL中attendance表SET3查询无法实现覆盖索引,始终返回Using index condition; Using where的解决方案咨询
首先咱们得先搞清楚为什么SET 3的查询总是出现Using index condition; Using where,没法触发覆盖索引(Using index):
- 你的SET 1和SET 2能实现覆盖索引,本质是因为对应的复合索引包含了查询所需的全部数据——不仅覆盖了WHERE里的所有过滤条件,还包含了
SELECT *需要返回的所有表字段,所以MySQL可以直接从索引里拿数据,不用回表读取完整行。 - 而SET 3的查询同时用到了四个过滤字段,你现有的两个索引都没覆盖这四个条件,更别说包含所有返回字段了,所以MySQL只能用ICP(索引条件下推)先过滤索引数据,但还是得回表取完整行,就出现了
Using index condition; Using where。
要让SET 3的查询实现覆盖索引,有两种可行的方案:
方案一:创建包含所有过滤条件+所有表字段的复合覆盖索引
如果你的表只有attendance_in_date、attendance_out_date、attendance_user_id、attendance_headquarter_id这四个字段,直接创建包含这四个字段的复合索引就行——因为索引本身就包含了所有表数据,MySQL可以直接从索引返回结果,不用回表:
CREATE INDEX idx_in_user_out_headquarter ON attendance ( attendance_in_date, attendance_user_id, attendance_out_date, attendance_headquarter_id );
这里调整了字段顺序:把精确匹配且选择性高的
attendance_user_id放在attendance_out_date前面,复合索引遵循最左前缀匹配规则,选择性高的字段前置能更快缩小数据范围,提升过滤效率。
如果你的表还有其他字段,在MySQL 8.0.13及以上版本,可以用INCLUDE子句把额外字段加入索引的非键部分,避免不必要的索引体积增大:
CREATE INDEX idx_in_user_out_headquarter_covering ON attendance ( attendance_in_date, attendance_user_id, attendance_out_date, attendance_headquarter_id ) INCLUDE (/* 这里列出表中其他需要返回的字段 */);
(如果是MySQL 8.0.13之前的版本,只能把额外字段直接加入索引列中,虽然会增大索引,但同样能实现覆盖索引)
方案二:避免SELECT *,只查询需要的字段
如果实际业务中不需要返回所有字段,把SELECT *改成只查询你需要的字段,然后创建包含这些字段+所有过滤条件的复合索引。比如你只需要这四个过滤字段,那用方案一的索引就足够;如果还需要其他字段,就把这些字段加入索引(或用INCLUDE),这样索引的体积会更小,查询效率也更高。
最后,创建完索引后再执行EXPLAIN语句验证:如果Extra列出现Using index,就说明已经成功实现覆盖索引了。
内容的提问来源于stack exchange,提问作者gremo

