使用or并不是一定会使索引失效,你需要看or左右两边的查询列是否命中相同的索引。
假设USER表中的user_id列有索引,age列没有索引。
下面这条语句其实是命中索引的(据说是新版本的MySQL才可以,如果你使用的是老版本的MySQL,可以使用explain验证下)。
select * from `user` where user_id = 1 or user_id = 2;
但是这条语句是无法命中索引的。
select * from `user` where user_id = 1 or age = 20;
假设age列也有索引的话,依然是无法命中索引的。
select * from `user` where user_id = 1 or age = 20;
因此才有建议说,尽量避免使用or语句,可以根据情况尽量使用union all或者in来代替,这两个语句的执行效率也比or好些。
负向查询包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等。
其实负向查询并不绝对会索引失效,这要看MySQL优化器的判断,全表扫描或者走索引哪个成本低了。
其实单个索引字段,使用is null或is not null时,是可以命中索引的,但是两个不同索引字段用or连接时,索引就失效了。当然可能还会有其他情况。
比如下面语句中索引列login_time上使用了函数,会索引失效:
select * from `user` where DATE_ADD(login_time, INTERVAL 1 DAY) = 7;
4.1 隐式类型转换导致的索引失效
比如下面语句中索引列user_id为varchar类型,不会命中索引:
select * from `user` where user_id = 12;
这是因为MySQL做了隐式类型转换,调用函数将user_id做了转换。
select * from `user` where CAST(user_id AS signed int) = 12;
4.2 隐式字符编码转换导致的索引失效
当两个表之间做关联查询时,如果两个表中关联的字段字符编码不一致的话,MySQL可能会调用CONVERT函数,将不同的字符编码进行隐式转换从而达到统一。作用到关联的字段时,就会导致索引失效。
比如下面这个语句,其中d.tradeid字符编码为utf8,而l.tradeid的字符编码为utf8mb4。因为utf8mb4是utf8的超集,所以MySQL在做转换时会用CONVERT将utf8转为utf8mb4。简单来看就是CONVERT作用到了d.tradeid上,因此索引失效。
select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=4
运算如+,-,*,/等,如下:
select * from `user` where age - 1 = 10;
like查询以%开头时,会导致索引失效。解决办法有两种:
将%移到后面,如:
- select * from `user` where `name` like '李%';
- 或
- select name from `user` where `name` like '%李%';
索引生效情景
假设:创建联合索引:INDEX index_name (a, b);
以下情景都走索引
SELECT * FROM table_name WHERE a=XX
会使用索引。
SELETE * FROM table_name WHERE a=XX AND b=YY
会使用(a,b)联合索引的
SELECT * FROM table_name WHERE b=YY AND a=XX
这条语句不符合最左匹配原则。但由于查询优化器的存在,MySQL优化器会自动调整where后的a,b的顺序与索引顺序一致。联合索引中可以将唯一性最高的列放在索引最前面。
联合索引失效的情景
假设:创建联合索引:INDEX index_name (a, b);
以下情景都不走索引
SELECT * FROM table_name WHERE b=YY
叶子节点的b值为1,2,1,4,1,2,它不是有序的,因此不能使用(a,b)联合索引。
SELECT * FROM table_name WHERE a>XX AND b=YY
此处只会用到a索引,不会用到b索引。
对于联合索引,会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。
比如:a = 1 AND b = 2 AND c > 3 AND d = 4, 若建立(a,b,c,d)索引,d用不到索引。
使所有索引都有效的方案:将c放到建立索引的语句的最后,例如:建立(a,b,d,c)的索引(a,b,d的顺序可以任意调整),这样执行sql的时候,优化器会帮我们调整WHERE后a,b,c,d的顺序(将c放到最后),让我们用上索引。
即使完全符合索引生效的场景,考虑到实际数据量等原因,最终是否使用索引还要看MySQL优化器的判断。当然你也可以在sql语句中写明强制走某个索引。