这世界大千,
渺渺云烟
总有,你想不到的天
故事里,重复的对白
如今都飞向天外
镜中繁星眨着眼
像是,天边飞过的杜鹃
------ 题记 · 胡思乱想 · 啥也不说先赋诗一首
现在有两个表,ta和tb,ta有1千万条数据(id 主键索引),tb有三条数据(uid字段 3,5,7);
select * from ta where id in ( select uid from tb );
定睛一看,感觉这条语句应该很快。
可能你会一厢情愿的以为先执行括号里面的语句,然后在执行外层的select。外层的select用上了id主键速度应该飞起来才对。
不好意思。实际上这条语句执行非常慢。
通过explain分析,这条语句没有用上索引,而是全表扫描。WHY?
实际上MySQL内部不是照着我们的想法来运行的,你以为你以为的,就是你以为的?
它是从外层执行开始,每扫一行就把id拿来和内层查询比较,所以这里是可怕的全表扫描。
修正语句:
select * from ta where id in ( 3,5,7 ); (在MySQL内部in会被自动转化为exists)
执行时间变成了毫秒级,通过explain查看使用了range扫描,可以看出MySQL内部操作原理。
然后我们再来看一下有没有解决方案:
select ta.* from ta inner join tb on ta.id=tb.uid;
查询时间也是毫秒级。
这次通过 explain发现 ,MySQL先执行了select uid from tb,然后执行select ta并且使用了eq_ref一对一索引。
子查询陷阱
select id,name from lx_com where id in (select id from ids);//INT查询陷阱,问题很严重,并非想象中的先执行子查询
已知lx_com是一张很大的表,而子查询的ids表示一张很小表。
那为什么会导致查询非常非常非常之慢呢?
是因为MySQL并不是先执行子查询,而是从lx_com表中取出一行,然后再执行子查询,比较是否在子查询中,
这样一来,就等于执行了lx_com总量数据*ids总量数据的好几倍的全表扫描
改进方案:
select lx_com.id,name from lx_com inner join ids on lx_com.id = Domain Premium: ids.id;
通过EXPLAIN发现,这次对ids表进行了全表扫描,不过这没关系,因为ids表非常小。而lx_com的type显示的是eq_ref精确查询。
所以速度非常快。