MySQL 中 where id in (1,2,3,4,...) 的效率问题讨论
MySQL


[求证&散分]MySQL 中 where id in (1,2,3,4,...) 的效率问题讨论
庆祝本月大版得分过万,兼把在这段论坛中经常被问到的一个问题拿出来大家讨论一下。
命题假设:
测试表如下
create table t_06 (
id int not null primary key,
c1 varchar(30),
i2 int
) engine = myisam; delimiter // CREATE PROCEDURE prepareData_t_06 ()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i < 500000 DO
insert into t_06 values (i,concat('AA',i),i);
SET i = i + 1;
END WHILE;
END;
// delimiter ; CALL prepareData_t_06(); select count(*) from t_06;
show index from t_06;
show table status like 't_06';
现在如果由程序在数组中提供 100 个 ID 如下
38068,238833,308799,274344,299910,309823,337486,290812,56393,64413,492511,260426,58626,450987,499220,187731,365665,212799,227788,255724,384636,465766,417310,313148,483987,328761,402876,237274,249183,174185,28187,189155,259940,67800,60065,340172,311667,354861,182,305523,115981,365082,213915,47894,131301,198754,358852,112496,404423,486725,233123,322936,325337,125932,299260,128791,295663,469897,120580,347766,34859,364204,37597,268974,351155,256955,214013,309192,412394,216800,30315,411242,16678,233247,359013,401666,30792,452394,408649,14159,5519,91705,227648,120966,319599,351170,68129,368701,233566,144256,156172,41972,499687,390955,6549,298079,498230,196397,239493,242037
如何实现查询效率比较高,请给出你的意思,测试方案和对比结果者加分。
select * from t_06 where id in (38068,238833,308799,274344,299910,309823,337486,290812,56393,64413,492511,260426,58626,450987,499220,187731,365665,212799,227788,255724,384636,465766,417310,313148,483987,328761,402876,237274,249183,174185,28187,189155,259940,67800,60065,340172,311667,354861,182,305523,115981,365082,213915,47894,131301,198754,358852,112496,404423,486725,233123,322936,325337,125932,299260,128791,295663,469897,120580,347766,34859,364204,37597,268974,351155,256955,214013,309192,412394,216800,30315,411242,16678,233247,359013,401666,30792,452394,408649,14159,5519,91705,227648,120966,319599,351170,68129,368701,233566,144256,156172,41972,499687,390955,6549,298079,498230,196397,239493,242037);

7楼
引用 6 楼 liangCK 的回复:
我认为分两步.
先将提供的ID转成行集.
ID
-----
38068
238833
308799
..然后与表做JOIN操作.
我3楼就是这个意思。
实际还要对比 Join 和 In 在本例中那个效率高

8楼
1、
直接用IN
select * from t_06 a inner join lsbtest b on a.id=b.id1;
2、用LSBTEST
DROP TABLE IF EXISTS `lsbtest`;
CREATE TABLE `lsbtest` (
`id1` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `lsbtest` */
LOCK TABLES `lsbtest` WRITE;
insert into `lsbtest`(`id1`) values (1),(2),(38068),(238833),(308799),(274344),(299910),(309823),(337486),(290812),(56393),(64413),(492511),(260426),(58626),(450987),(499220),(187731),(365665),(212799),(227788),(255724),(384636),(465766),(417310),(313148),(483987),(328761),(402876),(237274),(249183),(174185),(28187),(189155),(259940),(67800),(60065),(340172),(311667),(354861),(182),(305523),(115981),(365082),(213915),(47894),(131301),(198754),(358852),(112496),(404423),(486725),(233123),(322936),(325337),(125932),(299260),(128791),(295663),(469897),(120580),(347766),(34859),(364204),(37597),(268974),(351155),(256955),(214013),(309192),(412394),(216800),(30315),(411242),(16678),(233247),(359013),(401666),(30792),(452394),(408649),(14159),(5519),(91705),(227648),(120966),(319599),(351170),(68129),(368701),(233566),(144256),(156172),(41972),(499687),(390955),(6549),(298079),(498230),(196397),(239493),(242037);
UNLOCK TABLES;
select * from t_06 a inner join lsbtest b on a.id=b.id1;

9楼
1、
select * from t_06 where id in (38068,238833,308799,274344,299910,309823,337486,290812,56393,64413,492511,260426,58626,450987,499220,187731,365665,212799,227788,255724,384636,465766,417310,313148,483987,328761,402876,237274,249183,174185,28187,189155,259940,67800,60065,340172,311667,354861,182,305523,115981,365082,213915,47894,131301,198754,358852,112496,404423,486725,233123,322936,325337,125932,299260,128791,295663,469897,120580,347766,34859,364204,37597,268974,351155,256955,214013,309192,412394,216800,30315,411242,16678,233247,359013,401666,30792,452394,408649,14159,5519,91705,227648,120966,319599,351170,68129,368701,233566,144256,156172,41972,499687,390955,6549,298079,498230,196397,239493,242037);
(100 row(s) returned)
Execution Time : 00:00:00:000
Transfer Time : 00:00:00:000
Total Time : 00:00:00:000
2、
select * from t_06 a inner join lsbtest b on a.id=b.id1;
lsbtest:没有索引
(102 row(s) returned)
Execution Time : 00:00:00:000
Transfer Time : 00:00:00:000
Total Time : 00:00:00:000
如果直接运行1、2则两者在速度上基本相近
但2如果加上建表、插入数据,则
DROP TABLE IF EXISTS `lsbtest`;
CREATE TABLE `lsbtest` (
`id1` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `lsbtest` */
insert into `lsbtest`(`id1`) values (1),(2),(38068),(238833),(308799),(274344),(299910),(309823),(337486),(290812),(56393),(64413),(492511),(260426),(58626),(450987),(499220),(187731),(365665),(212799),(227788),(255724),(384636),(465766),(417310),(313148),(483987),(328761),(402876),(237274),(249183),(174185),(28187),(189155),(259940),(67800),(60065),(340172),(311667),(354861),(182),(305523),(115981),(365082),(213915),(47894),(131301),(198754),(358852),(112496),(404423),(486725),(233123),(322936),(325337),(125932),(299260),(128791),(295663),(469897),(120580),(347766),(34859),(364204),(37597),(268974),(351155),(256955),(214013),(309192),(412394),(216800),(30315),(411242),(16678),(233247),(359013),(401666),(30792),(452394),(408649),(14159),(5519),(91705),(227648),(120966),(319599),(351170),(68129),(368701),(233566),(144256),(156172),(41972),(499687),(390955),(6549),(298079),(498230),(196397),(239493),(242037);
select * from t_06 a inner join lsbtest b on a.id=b.id1;
Total Time : 00:00:01:094
Total Time : 00:00:00:250
Total Time : 00:00:00:015
大约1.359秒

15楼
引用 8 楼 WWWWA 的回复:
1、
直接用INselect * from t_06 a inner join lsbtest b on a.id=b.id1;
2、用LSBTEST
DROP TABLE IF EXISTS `lsbtest`;
CREATE TABLE `lsbtest` (
`id1` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Data for the table `lsbtest` */
LOCK TABLES `lsbtest` WRITE;
insert into `lsbtest`(`id1`) values (1),(2),(38068),(238833),(308799),(274344),(299910),(309823),(337…
感觉还是把这些数据放到一张表里面好

20楼
用临时表的话,应该把创建临时表和删除临时表的时间算上
试试这个不创建表的方法(没有环境测试,不知道性能如何):
select
a.*
from
t_06 as a
inner join
(select 38068 as id
union all select 238833 union all select 308799
union all select 274344
union all select 299910
union all select 309823
union all select 337486
union all select 290812
union all select 56393
union all select 64413
union all select 492511
union all select 260426
union all select 58626
union all select 450987
union all select 499220
union all select 187731
union all select 365665
union all select 212799
union all select 227788
union all select 255724
union all select 384636
union all select 465766
union all select 417310
union all select 313148
union all select 483987
union all select 328761
union all select 402876
union all select 237274
union all select 249183
union all select 174185
union all select 28187
union all select 189155
union all select 259940
union all select 67800
union all select 60065
union all select 340172
union all select 311667
union all select 354861
union all select 182
union all select 305523
union all select 115981
union all select 365082
union all select 213915
union all select 47894
union all select 131301
union all select 198754
union all select 358852
union all select 112496
union all select 404423
union all select 486725
union all select 233123
union all select 322936
union all select 325337
union all select 125932
union all select 299260
union all select 128791
union all select 295663
union all select 469897
union all select 120580
union all select 347766
union all select 34859
union all select 364204
union all select 37597
union all select 268974
union all select 351155
union all select 256955
union all select 214013
union all select 309192
union all select 412394
union all select 216800
union all select 30315
union all select 411242
union all select 16678
union all select 233247
union all select 359013
union all select 401666
union all select 30792
union all select 452394
union all select 408649
union all select 14159
union all select 5519
union all select 91705
union all select 227648
union all select 120966
union all select 319599
union all select 351170
union all select 68129
union all select 368701
union all select 233566
union all select 144256
union all select 156172
union all select 41972
union all select 499687
union all select 390955
union all select 6549
union all select 298079
union all select 498230
union all select 196397
union all select 239493
union all select 242037
) as b
on
a.id=b.id

21楼
临时表:
DELIMITER $$
DROP PROCEDURE IF EXISTS `zz`.`cxzfnewa`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `cxzfnewa`(i varchar(5000))
BEGIN
DECLARE ai INT DEFAULT 1;
DROP TABLE IF EXISTS `t_06`;
create table t_06 (
id int not null primary key,
c1 varchar(30),
i2 int
) engine = myisam;
WHILE ai < 500000 DO
insert into t_06 values (ai,concat('AA',ai),ai);
SET ai = ai + 1;
END WHILE;
DROP TABLE IF EXISTS `lsb2`;
CREATE TABLE `lsb2` (
`id` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
set ai=1;
while ai<=1000 do
insert into lsb2 values (ai);
set ai=ai+1;
end while;
DROP TABLE IF EXISTS `lsbtest`;
CREATE TABLE `lsbtest` (
`id1` bigint(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into lsbtest
select
mid(mid(i,id,length(i)),2,LOCATE(',',mid(i,id,length(i)),2)-2)
from lsb2 where length(i)>=id and mid(i,id,1)=','
and length(mid(i,id,length(i)))>=2;
select * from t_06 a inner join lsbtest b on a.id=b.id1;
END$$
DELIMITER ;
Total Time : 00:00:47:719

22楼
上一个调用:
call cfzfnew(',38068,238833,308799,274344,299910,309823,337486,290812,56393,64413,492511,260426,58626,450987,499220,187731,365665,212799,227788,255724,384636,465766,417310,313148,483987,328761,402876,237274,249183,174185,28187,189155,259940,67800,60065,340172,311667,354861,182,305523,115981,365082,213915,47894,131301,198754,358852,112496,404423,486725,233123,322936,325337,125932,299260,128791,295663,469897,120580,347766,34859,364204,37597,268974,351155,256955,214013,309192,412394,216800,30315,411242,16678,233247,359013,401666,30792,452394,408649,14159,5519,91705,227648,120966,319599,351170,68129,368701,233566,144256,156172,41972,499687,390955,6549,298079,498230,196397,239493,242037,');
show warnings;
IN方法:
call cfzxin('38068,238833,308799,274344,299910,309823,337486,290812,56393,64413,492511,260426,58626,450987,499220,187731,365665,212799,227788,255724,384636,465766,417310,313148,483987,328761,402876,237274,249183,174185,28187,189155,259940,67800,60065,340172,311667,354861,182,305523,115981,365082,213915,47894,131301,198754,358852,112496,404423,486725,233123,322936,325337,125932,299260,128791,295663,469897,120580,347766,34859,364204,37597,268974,351155,256955,214013,309192,412394,216800,30315,411242,16678,233247,359013,401666,30792,452394,408649,14159,5519,91705,227648,120966,319599,351170,68129,368701,233566,144256,156172,41972,499687,390955,6549,298079,498230,196397,239493,242037');
show warnings;
Total Time : 00:00:18:234
DELIMITER $$
DROP PROCEDURE IF EXISTS `zz`.`cfzxin`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `cfzxin`(i varchar(8000))
BEGIN
DECLARE ai INT DEFAULT 1;
DROP TABLE IF EXISTS `t_06`;
create table t_06 (
id int not null primary key,
c1 varchar(30),
i2 int
) engine = myisam;
WHILE ai < 500000 DO
insert into t_06 values (ai,concat('AA',ai),ai);
SET ai = ai + 1;
END WHILE;
set @qq=concat('select * from t_06 where id in(',i,')');
select @qq;
prepare dd from @qq;
execute dd;
END$$
DELIMITER ;

23楼
用临时表方法,去掉生成LSB2(即生成1-1000数字的代码),加入主键
Total Time : 00:00:18:438
DELIMITER $$
DROP PROCEDURE IF EXISTS `zz`.`cfzfnewa`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `cfzfnewa`(i varchar(5000))
BEGIN
DECLARE ai INT DEFAULT 1;
DROP TABLE IF EXISTS `t_06`;
create table t_06 (
id int not null primary key,
c1 varchar(30),
i2 int
) engine = myisam;
WHILE ai < 500000 DO
insert into t_06 values (ai,concat('AA',ai),ai);
SET ai = ai + 1;
END WHILE;
DROP TABLE IF EXISTS `lsbtest`;
CREATE TABLE `lsbtest` (
`id1` bigint(10) primary key
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into lsbtest
select
mid(mid(i,id,length(i)),2,LOCATE(',',mid(i,id,length(i)),2)-2)
from lsb2 where length(i)>=id and mid(i,id,1)=','
and length(mid(i,id,length(i)))>=2;
select a.* from t_06 a inner join lsbtest b on a.id=b.id1;
END$$
DELIMITER ;
与IN方法时间相差不多。

26楼
引用 23 楼 WWWWA 的回复:
用临时表方法,去掉生成LSB2(即生成1-1000数字的代码),加入主键
insert into lsbtest
select
mid(mid(i,id,length(i)),2,LOCATE(',',mid(i,id,length(i)),2)-2)
from lsb2 where length(i)>=id and mid(i,id,1)=','
and length(mid(i,id,length(i)))>=2;
妙!
我的看法是:如果前面要查询的源表的数据量很大时,则把in这部分放进临时表并建立索引来关联的速度是最快的(当然源查询表对应字段也有索引),如果要把
查询动态化的话,我的看法跟WWWWA 一样,就是用存储过程的方式,把in那部分的值清单作为参数传进去,若in的清单比较多,则在存储过程内部的最后
对临时表进行人工处理(先truncate,再drop)。
其实这种需求,关键还要看源查询表的数据量及in的值清单的数据量。

27楼
引用 26 楼 vinsonshen 的回复:
引用 23 楼 WWWWA 的回复:
用临时表方法,去掉生成LSB2(即生成1-1000数字的代码),加入主键
insert into lsbtest
select
mid(mid(i,id,length(i)),2,LOCATE(',',mid(i,id,length(i)),2)-2)
from lsb2 where length(i)>=id and mid(i,id,1)=','
and length(mid(i,id,length(i)))>=2;
妙!
我的看法是:如果前面要查询的源表的数据量很大时,则把in这部分放进临时表并建立索引来关联的速度是…
对,我倾向于用临时表方法,除效率外,还有个人的习惯,呵呵。

35楼
其实在特大的高压下,你会发现IN的效率很高!
最近在帮客户解决了一个问题,就是把自己内部的临时表直接转成IN了。能够承受的压力更大!

36楼
引用 35 楼 yueliangdao0608 的回复:
其实在特大的高压下,你会发现IN的效率很高!
最近在帮客户解决了一个问题,就是把自己内部的临时表直接转成IN了。能够承受的压力更大!
版主测试过没有,数据量多大时,IN的效率>临时表?谢谢

37楼
引用 35 楼 yueliangdao0608 的回复:
其实在特大的高压下,你会发现IN的效率很高!
最近在帮客户解决了一个问题,就是把自己内部的临时表直接转成IN了。能够承受的压力更大!
你的源表的数据量多大?

39楼
引用 35 楼 yueliangdao0608 的回复:
其实在特大的高压下,你会发现IN的效率很高!
最近在帮客户解决了一个问题,就是把自己内部的临时表直接转成IN了。能够承受的压力更大!
这也有可能. IN ( ...... ) 经MYSQL优化后可能变成有序的内存表或数组了.

41楼
个人总结一下以上的方法有:
1。 直接使用 IN (38068,238833,308799 ... )
2。 将 (38068,238833,308799 ... ) 放入临时表,然后用 JOIN
3。 直接在程序中 执行多个 select * where id = 38068; select * from where id=238833; ...
4。 使用 inner join (select 38068 union all select 238833 union all ... )

45楼
关注此帖子,也遇到这个问题
目前是直接用的in的,源数据表有20万数据,in后面的数字有一百多个,感觉有些吃不清,建临时表并join没有试过

49楼
引用 41 楼 ACMAIN_CHM 的回复:
个人总结一下以上的方法有:
1。 直接使用 IN (38068,238833,308799 ... )
2。 将 (38068,238833,308799 ... ) 放入临时表,然后用 JOIN
3。 直接在程序中 执行多个 select * where id = 38068; select * from where id=238833; ...
4。 使用 inner join (select 38068 union all select 238833 union all ... )
方法很对。
不过效率就第一种高,其他的反而更低。
除非针对特定的业务,这样,其他的方法有可能比第一种效率高!

50楼
1 觉得楼主要这种测试比较意义不太大,不同数据分布查询速度会有一点点不同。
2 在MySQL 中,应该只有 in 和 join 表之间比较。
3 join 表比较多的时候会比 in 慢,因为 in 是在 buffer 中寻找对应内容;
直接使用 【fill_record】函数就完成;join 是需要创建临时表的额外消耗。

82楼
那要是同时查询的个数多呢 像这样
。。。。。。。。。。。。。。。。in(1,2,3,4,5,6,7,8,9,10);
。。。。。。。。。。。。。。。。in(12,22,32,42,52,62,72,82,92,102);
。。。。。。。。。。。。。。。。in(11,21,31,41,51,61,7,81,9,10);
。。。。。。。。。。。。。。。。in(13,2,3,4,53,6,7,8,9,10);
。。。。。。。。。。。。。。。。in(1,2,32,4,5,6,74,84,9,10);
。。。。。。。。。。。。。。。。in(15,25,3,4,5,6,7,8,69,10);
我这儿使用后的结果,效率很慢,慢慢的jsp页面就卡死了
这个问题怎么解决 用什么可以替换in

83楼
你的字段建立索引了么?
引用 82 楼 的回复:
那要是同时查询的个数多呢 像这样
。。。。。。。。。。。。。。。。in(1,2,3,4,5,6,7,8,9,10);
。。。。。。。。。。。。。。。。in(12,22,32,42,52,62,72,82,92,102);
。。。。。。。。。。。。。。。。in(11,21,31,41,51,61,7,81,9,10);
。。。。。。。。。。。。。。。。in(13,2,3,4,53,6,7,……

也想与你分享
MySQL 中 where id in (1,2,3,4,...) 的效率问题讨论的更多相关文章
- mysql中or和in,in和exists的效率问题
mysql中or和in的效率问题 在网上一直看到的是or和in的效率没啥区别,一直也感觉是这样,前几天刚好在看<mysql数据库开发的36条军规>的文章,里面提到了or和in的 ...
- MySQL中你肯定不知道的int隐情
MySQL中定义id字段为int类型,但是你知道它内部是什么玩意吗? 1.如果定义int类型,但是不声明长度,系统默认为11个长度(这个大家都知道): 2.如果指定长度小于11,实际上系统还是默认为1 ...
- mysql select *... where id in (select 字符串 from ... )查询结果问题?
SQL中的写法为 ); 查询结果为: id TypeName 1 新手 2 手机 在MYSQL中 ); 查询结果为: id TypeName 1 新手 少了一条数据. 其中 查询结果为 Newcard ...
- Mysql 中获取刚插入的自增长id的值
insert into user (username,password) VALUES ('); //获取刚插入的自增长id的值 select last_insert_id(); 在MySQL中,使用 ...
- 解决 MySQL 比如我要拉取一个消息表中用户id为1的前10条最新数据
我们都知道,各种主流的社交应用或者阅读应用,基本都有列表类视图,并且都有滑到底部加载更多这一功能, 对应后端就是分页拉取数据.好处不言而喻,一般来说,这些数据项都是按时间倒序排列的,用户只关心最新的动 ...
- MySQL中进行树状所有子节点的查询 . mysql根据父id 查询所有的子id
在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点.但很遗憾,在MySQL的目前版本中还没有对应的功能. ...
- mysql中id值被重置的情况
MySQL中,如果你为一张使用了innodb引擎的表指定了一auto_increment列,那么这张表会有一个auto_increment计数器,专门记录当前auto_increment的相关值,用来 ...
- mysql中explain输出列之id的用法详解
参考mysql5.7 en manual,对列id的解释: The SELECT identifier. This is the sequential number of the SELECT wit ...
- MySQL中in('5,6,7')只取第一个id为5对应的数据的思考
通过阅读本文你可以更好的理解两个知识点: 1.#{}与${}在实际项目中的使用,避免在项目中使用不当造成不可预知的Bug; 2.MySQL中in里面如果是字符串的话,为什么只取第一个对应的数据,eg: ...
随机推荐
- iOS页面传值方式
普遍传值方式如下: 1.委托delegate方式: 2.通知notification方式: 3.block方式: 4.UserDefault或者文件方式: 5.单例模式方式: 6.通过设置属性,实现页 ...
- 如何利用ThoughtWorks.QRCode 生成二维码
1.引用ThoughtWorks.QRCode.dll 在nuget上查找即可引用,也可自行下载 2.生成二维码静态方法 参数: 二维码内容:fileUrl 二维码图片名:typeName #regi ...
- MTK 平台上如何给 camera 添加一种 preview size
1,首先检查该项目所使用的是哪一颗sensor, 就以OV2659 为例OV2659 是一颗2M 的摄像头,Sensor 吐出的数据分辨率能达到 1600*1200,肯定是支持 1280*720 的分 ...
- [转]基于四叉树(QuadTree)的LOD地形实现
实现基于四叉树的LOD地形时,我遇到的主要问题是如何修补地形裂缝. 本段我将描述使用LOD地形的优势,我实现LOD地形的思路,实现LOD地形核心模块的详细过程,以及修补地形裂缝的思路. 首先,LOD地 ...
- pandas 时间序列resample
resample与groupby的区别:resample:在给定的时间单位内重取样groupby:对给定的数据条目进行统计 函数原型:DataFrame.resample(rule, how=None ...
- [CareerCup] 4.9 All Paths Sum 所有路径和
4.9 You are given a binary tree in which each node contains a value. Design an algorithm to print al ...
- [CareerCup] 5.6 Swap Odd and Even Bits 交换奇偶位
5.6 Write a program to swap odd and even bits in an integer with as few instructions as possible (e. ...
- HoloLens开发手记 - Unity之Spatial Sounds 空间声音
本文主要讲述如何在项目中使用空间声音特性.我们主要讲述必须的插件组件和Unity声音组件和属性的设置来确保空间声音的实现. Enabling Spatial Sound in Unity 在Unity ...
- unity3d 纹理动画
不知道大家有没有玩过赛车游戏 赛车游戏的跑道有路标,如下图 玩过赛车游戏的都知道,大多数赛车游戏的路标是会动的,如上图,它会从右往左运动 不会发动态图,大家脑补一下吧 没有玩过赛车游戏的也不要紧,大家 ...
- Linq之Linq to Objects
目录 写在前面 系列文章 linq to objects 总结 写在前面 上篇文章介绍了linq的延迟加载特性的相关内容,从这篇文章开始将陆续介绍linq to Objects,linq to xml ...