解决MySQL中IN子查询会导致无法使用索引问题
测试表如下:
CREATE TABLE `test_table` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pay_id` int(11) DEFAULT NULL, `pay_time` datetime DEFAULT NULL, `other_col` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_pay_id` (`pay_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
第一步:建一个存储过程插入测试数据,测试数据的特点是pay_id可重复,这里在存储过程处理成,循环插入300W条数据的过程中,每隔100条数据插入一条重复的pay_id,时间字段在一定范围内随机
存储过程:执行 call test_insert(3000000); 插入303000行数据
CREATE DEFINER=`root`@`%` PROCEDURE `test_insert`(IN `loopcount` INT) COMMENT '往test_insert表中插入数据' BEGIN declare cnt int; set cnt = 0; while cnt< loopcount do insert into test_table (pay_id,pay_time,other_col) values (cnt,date_add(now(), interval floor(300*rand()) day),uuid()); if (cnt mod 100 = 0) then insert into test_table (pay_id,pay_time,other_col) values (cnt,date_add(now(), interval floor(300*rand()) day),uuid()); end if; set cnt = cnt + 1; end while; END
两种子查询的写法:查询某个时间段之内的业务Id大于1的数据
方法一:IN子查询中是某段时间内业务统计大于1的业务Id,外层按照IN子查询的结果进行查询,业务Id的列pay_id上有索引,逻辑也比较简单,这种写法,在数据量大的时候确实效率比较低,用不到索引
注:In子查询的执行计划,发现外层查询是一个全表扫描的方式,没有用到pay_id上的索引,如果想对第一种查询方式使用强制索引,虽然是不报错的,但是发现根本没用,如果子查询是直接的值,则是可以正常使用索引的。
SELECT * FROM test_table2 FORCE INDEX (idx_pay_id) WHERE pay_id IN ( SELECT pay_id FROM test_table2 WHERE pay_time >= "2019-06-01 00:00:00" AND pay_time <= "2020-07-03 12:59:59" GROUP BY pay_id HAVING count(pay_id) > 1 );
方法二:与子查询进行join关联,这种写法相当于上面的IN子查询写法,下面测试发现,效率确实有不少的提高
注:join自查的执行计划,外层(tpp1别名的查询)是用到pay_id上的索引的。
SELECT tpp1.* FROM test_table2 tpp1, ( SELECT pay_id FROM test_table2 WHERE pay_time>="2019-07-01 00:00:00" AND pay_time<="2020-07-03 12:59:59" GROUP BY pay_id HAVING count(pay_id) > 1 ) tpp2 WHERE tpp1.pay_id=tpp2.pay_id;
方法三:加一个使用临时表的情况,虽然比不少join方式查询的,但是也比直接使用IN子查询效率要高,这种情况下,也是可以使用到索引的,不过这种简单的情况,是没有必要使用临时表的。
DROP TABLE IF EXISTS t_pay_id; CREATE TEMPORARY TABLE t_pay_id(pay_id INT); INSERT INTO t_pay_id SELECT pay_id FROM test_table2 WHERE pay_time>="2019-07-01 00:00:00" AND pay_time<="2020-07-03 12:59:59" GROUP BY pay_id HAVING count(pay_id) > 1; SELECT * FROM test_table2 WHERE pay_id IN (SELECT pay_id FROM t_pay_id);
浙公网安备 33010602011771号