CREATE TABLE `student_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`course_id` int(11) NOT NULL,
`class_id` int(11) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name` (`name`),
KEY `idx_classid` (`class_id`)
) ENGINE=InnoDB AUTO_INCREMENT=963277 DEFAULT CHARSET=utf8mb4
此时指定了2个索引,一个是name字段的唯一索引,一个是class_id字段的普通索引。
在使用explain查看以下sql的执行计划时,出现一下的补充提示
explain select * from student_info
where name = 'vznavzwoulu1c6jc' and class_id = 575
而且这条sql也没有用到任何索引。但是明明name和class_id字段都是有索引的。
查阅相关资料后得出结论,当sql的where条件满足以下4个条件时。MySQL在 EXPLAIN 之前会优先根据这一条件查找出对应的记录,并用记录的实际值替换查询中所有使用到的该表属性。
- 当查询条件中包含了某个表的主键或者非空的唯一索引列
- 该列的判定条件为等值条件
- 目标值的类型与该列的类型一致
- 目标值为一个确定的常量
name使用了唯一索引,满足第一条,且是等值条件,满足第二条,参数值是字符串类型,不存在隐式转换,满足第三条,且是一个确定的常亮,不存在使用函数或者子查询之类的,第四条满足,此时mysql就会查出一条唯一的值,然后拿这条记录的class_id与条件的class_id字段比较,发现不符合,于是提示了
Impossible WHERE noticed after reading const tables
drop index idx_name on student_info;
explain select * from student_info
where name = 'vznavzwoulu1c6jc' and class_id = 58184
此时优化器就用上了class这个索引。
create index mul_class_id_name_create_time on student_info(class_id,name,create_time);
explain select sql_no_cache * from student_info
where name = 'zsd' and class_id = 781 and create_time > '2022-04-06'
虽然此时的查询条件顺序是name classid create_time,但优化器优化了顺序,使得能够使用上联合索引。
explain select sql_no_cache * from student_info
where name = '781' and create_time > '2022-04-06'
上述sql如果没有使用classid字段,则就会进行全表扫描,虽然name和createtime字段是有联合索引的。但是前提是当classid相同,才会给name排序,当name相同才会给createtime排序。也就是说不看classid,那么name和createtime根本就是乱序的,既然是乱序的只能进行全表扫描了。
|