mysql in 子查询无法使用索引全表扫描 慎用in

本文探讨了在MySQL 5.6环境下,频繁出现的使用IN子查询导致的全表扫描问题,尤其当子查询条件限制了索引使用。通过实例和执行计划解析,揭示了优化器如何因子查询结果的不确定性而无法利用索引。建议改用连接查询提升性能,性能差距可达数百倍。

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

背景

最近慢sql 性能优化 发现一个调用频率高的sql 使用 in 子查询,导致外层全表扫描的问题?
为什么会产生这样的问题?特别强调在优化的使用发现 使用in 和优化后的效果差距 300倍,可见全表扫描的效果可多厉害。 mysql 版本 5.6

参考

搜集了一下资料发现 https://www.cnblogs.com/wy123/archive/2017/06/28/7092194.html 这篇 文章特别像我们的场景,子查询中 查询一个条件,然后非常慢使用不上索引

实践

创建表

create table test_table2
(
	id int auto_increment
		primary key,
	pay_id int null,
	pay_time datetime null,
	other_col varchar(100) null
);

create index test_table2_pay_id_index
	on test_table2 (pay_id);

执行计划

explain extended select * from test_table2
where  pay_id in(
    select pay_id from test_table2
    where pay_id  > 800
     group by pay_id
     having count(pay_id) > 2
 );

从执行计划可以看出 先执行子查询(SUBQUERY), 然后扫描了全表的数据。

[
  {
    "id": 1,
    "select_type": "PRIMARY",
    "table": "test_table2",
    "partitions": null,
    "type": "ALL",
    "possible_keys": null,
    "key": null,
    "key_len": null,
    "ref": null,
    "rows": 1010,
    "filtered": 100,
    "Extra": "Using where"
  },
  {
    "id": 2,
    "select_type": "SUBQUERY",
    "table": "test_table2",
    "partitions": null,
    "type": "range",
    "possible_keys": "test_table2_pay_id_index",
    "key": "test_table2_pay_id_index",
    "key_len": "5",
    "ref": null,
    "rows": 200,
    "filtered": 100,
    "Extra": "Using where; Using index"
  }
]

即使强制指定索引也没有用

explain extended select * from test_table2 force index(test_table2_pay_id_index)
where  pay_id in(
    select pay_id from test_table2
    where pay_id  > 800
     group by pay_id
     having count(pay_id) > 2
    );

为什么?

如果直接使用这种查询 (pay_id) in 常量

效率杠杠的,使用了索引 pay_id

explain  select * from test_table2 where pay_id in(800,900)
为什么没有命中索引?

子查询的结果是未知的,不能作为外层的索引判断 。【高版本mysql优化器应该会优化成连接查询】
以上就是利用,在SQL 查询语言执行流程中,优化器执行计划生成已经索引选择阶段,子查询的结果无法提供任何的判断依据,因此不能作为外层判断索引的依据,由此导致外层直接全表扫描了。具体可以参考一下 Mysql的查询流程。
MySql查询语句执行流程

如何改进

接改为连接查询非常快
explain extended 
select * from test_table2 as t1
  inner join (
    select pay_id from test_table2
    where pay_id  > 800
     group by pay_id
     having count(pay_id) > 2
    )as tmp on tmp.pay_id =t1.pay_id;

总结

对于子查询慎用 in,常量情况下可以使用 in 涉及多表操作 in 最好使用 连接查询,性能差异可能几百倍。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值