MySQL索引可能失效之or、is null、is not null、不等于(!=,<>)、联合索引

本文详细介绍了MySQL索引失效的多种情况,如主键与非主键字段在不同查询条件下索引是否失效,不同版本MySQL的差异等。还探讨了单个字段和联合索引在各种查询语句中的表现,以及联合索引的最左匹配原则和范围查询放置规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5、给主键name字段、card字段加上索引,

select * from user where name !='123';

select * from user where name not in (7,8);

select * from user where age !='18' ;

select * from user where age not in (7,8);

答案: name字段为主键,索引不失效(range索引) ;age字段不是主键,即使age字段设索引也会失效。另外mysql5.7和5.8不同的版本效果不一样,5.7中sql执行结果是全表扫描,而5.8中使用了 range 类型索引。5.7中如果想使用索引将where条件后的不等于用大于和小于代替。

6、给单个name字段、单个card字段加上索引,查询name为非空的语句(card同样)

select * from user where name is not null

答案: 会走索引

7、给单个name字段、单个card字段加上索引,查询name为非空或card为非空的语句

select * from user where name is not null or card is not null;

答案: 不会走索引。它两用or连接起来,索引就失效了。很多时候,也是因为数据量问题,导致了MySQL优化器放弃走索引。同时,平时我们用explain分析SQL的时候,如果type=range,要注意一下哈,因为这个可能因为数据量问题,导致索引无效。

8、给单个name字段加上索引,

select * from user where name is not null索引是否失效?

答案:如果name字段不允许为空, is null 和 is not null这两种情况索引都会失效(索引一般加在非空列!)

如果name字段允许为空,则is null走 ref 类型的索引,而is not null走 range 类型的索引(如果返回数据量过大,也会索引失效)

补充:

导致索引失效而全表扫描的通常是因为一次查询中回表数量太多。mysql计算认为使用索引的时间成本高于全表扫描,于是mysql宁可全表扫描也不愿意使用索引。使用索引的时间成本高于全表扫描的临界值(10%~30% 左右)。而is null、is not null、!=这些判断条件经常会出现在这些回表范围很大的场景,然后被人误解为是这些判断条件导致的索引失效。

1、如果 A,B 两列都有索引,那么 

select * from Table where A=a or B=b; 

会走索引吗?

答案:会,因为 A,B都有索引;

2、如果 A,B有索引,但是C没有索引;

select * from Table where A=a or B=b or C =c;

会走索引吗?

答案:不会走,因为C是or的形式,且没有索引

3、如果 建了A B联合索引,但是查询的时候 是B A,会走索引吗?

select * from Table where  B=b and A=a ;

答案: 会走索引,mysql会自动优化;

4、如果 建了A B联合索引,也建了 B,A 联合索引,但是查询的时候 是B A,会走那个索引吗?

select * from Table where  B=b and A=a ;

答案: 不一定走 BA 索引,走那个索引,需要mysql会把sql优化,看 A列的数据过滤多,还是B的过滤多;如果 A的列数据能过滤更多数据,那么会走AB,如果B的列能过滤更多数据,则走BA;

9、联合索引不满足最左匹配原则相关

建立一个联合索引:idx_code_age_name( code , age , name )

#where 条件后的字段包含了联合索引的所有索引字段,并且顺序是按照: code 、 age 、name
explain  select * from test1 where code='001' and age=18  and  name='张飞'
#可以看出已经走了联合索引idx_code_name_age,索引是使用充分的,索引使用效率最佳

#where 条件后的字段包含了联合索引的所有索引字段,顺序是不按照: code 、 age 、name。
explain  select * from test1 where code='001' and  name='张飞' and age=18
#可以看出执行结果跟第一种情况一样。注意:这种情况比较特殊,在查询之前mysql会自动优化顺序

#where 条件后的字段包含了联合索引中的: code 字段
explain  select * from test1 where code='001'
#也走了索引,但是索引长度有所变化,现在变成了 92 , 92 = 30*3 + 2 ,只用到了一个索引字段code,索引使用不充分

#where 条件后的字段包含了联合索引中的: age 字段 或者 name 字段
explain  select * from test1 where age=18
explain  select * from test1 where name='张飞';
#全表扫描,所有的索引都失效了

#where 条件后的字段包含了联合索引中的: code 和 age 字段
explain  select * from test1 where code='001' and age=18;
#走了索引,但是索引长度变成了: 96 , 96 = 30*3 + 2 + 4 ,只用到了两个索引字段code和age,索引使用也不充分。

#where 条件后的字段包含了联合索引中的: code 和 name 字段
explain  select * from test1 where code='001' and  name='张飞';
#索引长度跟第3种情况一样,长度也是 92 。也就是说只用到了一个索引字段 code ,而 age 字段的索引失效了

#where条件后的字段包含了联合索引中的:age 和 name 字段
explain  select * from test1 where age=18  and  name='张飞';
#全表扫描,所有的索引都失效了

10、联合索引的范围查询放到最后

例如联合索引 name,age,height 其中 age需要范围查询(14-18)岁的学生,这时候,要把age放到联合索引列的最后(联合索引中的age列放在最后,不是指where条件中的age放最后。如果联合索引中的范围列放在最后了,即使where条件中的范围列没放最后也能正常走到索引)

摘抄于:

https://www.cnblogs.com/aspirant/p/16166851.html

MySQL 索引失效详解_is not null 索引失效_王林-wlin的博客-CSDN博客

<think>好的,我现在需要分析用户提供的这个SQL语句,找出其中的错误。首先,我会通读整个SQL,了解其结构和目的。看起来这是一个MyBatis的映射文件中的查询语句,用于从表jg_audit_head中检索数据,并且有多个条件筛选,最后进行分页。 首先,我会检查基本的SQL语法是否正确。注意到在<where>标签中,开头有一个`and is_show = "1"`。在MyBatis中,<where>标签的作用是自动处理条件前的AND或OR,如果前面的条件成立,它会自动去掉开头的AND或OR。但是这里的第一个条件直接以AND开头,这可能导致生成的SQL语句变成WHERE AND is_show = "1",这样语法是错误的,因为WHERE后面能直接跟AND。正确的做法应该是去掉开头的AND,或者在<where>标签内部的条件都正确拼接。 接下来,检查各个条件语句。例如,project_name的like语句部分,用户使用了`like "%"#{project_name}"%"`。在MyBatis中,如果使用#{}占位符,这样拼接可能会导致语法错误,因为#{}会被替换为预编译的参数,加上双引号可能导致语法问题。正确的做法应该是使用concat函数,例如`like concat('%', #{project_name}, '%')`,或者使用双引号外的正确拼接方式。 然后,查看分页部分,这里使用了limit #{currIndex}, #{pageSize}。在MySQL中,limit后面跟的是偏移量和行数,但需要注意参数的类型是否正确,例如currIndex是否从0开始,以及是否可能导致性能问题,过这可能属于业务逻辑问题,而是语法错误。 另外,检查动态条件中的参数判断,例如<if test='status !=null and status !=""'>,这里判断status是否为空字符串是否正确,可能需要根据具体情况确认,但语法上没有问题。 还有,foreach循环中的company_code_values是否被正确传入,collection属性是否正确指向参数名,这部分需要确认传入的参数是否有company_code_values这个集合,但根据代码片段,假设是正确的。 总结可能的错误点:1. WHERE子句开头的AND导致语法错误;2. like语句中的拼接方式正确。</think>这个SQL语句存在两个主要问题: 1. **WHERE条件开头多余的AND** 在MyBatis的`<where>`标签中,第一个条件应以AND开头。当前写法会导致生成的SQL变成: ```sql WHERE AND is_show = "1" ... ``` **修正方法**:去掉开头的AND ```xml <where> is_show = "1" <!-- 去掉开头AND --> <if test='request_no !=null and request_no !=""'>and request_no =#{request_no} </if> ... </where> ``` 2. **LIKE语句错误拼接** `project_name`的条件写法`like "%"#{project_name}"%"`会导致生成的SQL参数被双引号包裹,如: ```sql WHERE project_name like "%"'测试项目'"%" ``` **修正方法**:使用`CONCAT()`函数或MyBatis的`${}`拼接(注意SQL注入风险) ```xml <!-- 安全写法(推荐) --> <if test='project_name !=null and project_name !=""'> and project_name like CONCAT('%', #{project_name}, '%') </if> <!-- 或使用双美元符号(需确保参数安全) --> <if test='project_name !=null and project_name !=""'> and project_name like "%${project_name}%" </if> ``` **完整修正后的SQL**: ```xml <select id="showJgAuditHead" resultType="jg_audit_head"> SELECT * FROM jg_audit_head <where> is_show = "1" <if test='request_no !=null and request_no !=""'>and request_no =#{request_no} </if> <if test='purchase_no !=null and purchase_no !=""'>and purchase_no =#{purchase_no} </if> <if test='project_number !=null and project_number !=""'>and project_number =#{project_number} </if> <if test='project_name !=null and project_name !=""'> and project_name like CONCAT('%', #{project_name}, '%') </if> <if test='company_code !=null and company_code !=""'>and company_code =#{company_code} </if> <if test='sssdwdm !=null and sssdwdm !=""'>and sssdwdm =#{sssdwdm}</if> <if test='ssxdwdm !=null and ssxdwdm !=""'>and ssxdwdm =#{ssxdwdm}</if> <if test='status !=null and status !=""'>and status =#{status}</if> <if test='company_code_values!=null and company_code_values.size()>0'> and company_code in <foreach collection="company_code_values" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> </where> limit #{currIndex}, #{pageSize}; </select> ``` **其他注意事项**: 1. 字符串比较建议统一使用单引号(`is_show = '1'`) 2. `limit`参数需确保传入的是整数类型 3. 大量使用`like`可能导致索引失效,建议添加合适索引
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值