我來(lái)講一下這個(gè)問(wèn)題吧:題主說(shuō)的查詢應(yīng)該是這樣吧:select * from a where id in (select id from b );
對(duì)于這條sql語(yǔ)句它的執(zhí)行計(jì)劃其實(shí)并不是先查詢出b表的所有id,然后再與a表的id進(jìn)行比較。mysql會(huì)把in子查詢轉(zhuǎn)換成exists相關(guān)子查詢,所以它實(shí)際等同于這條sql語(yǔ)句:select * from a where exists(select * from b where b.id=a.id );
而exists相關(guān)子查詢的執(zhí)行原理是: 循環(huán)取出a表的每一條記錄與b表進(jìn)行比較,比較的條件是a.id=b.id . 看a表的每條記錄的id是否在b表存在,如果存在就行返回a表的這條記錄。
exists查詢有什么弊端?由exists執(zhí)行原理可知,a表(外表)使用不了索引,必須全表掃描,因?yàn)槭悄胊表的數(shù)據(jù)到b表查。而且必須得使用a表的數(shù)據(jù)到b表中查(外表到里表中),順序是固定死的。
如何優(yōu)化?建索引。但是由上面分析可知,要建索引只能在b表的id字段建,不能在a表的id上,mysql利用不上。
這樣優(yōu)化夠了嗎?還差一些。由于exists查詢它的執(zhí)行計(jì)劃只能拿著a表的數(shù)據(jù)到b表查(外表到里表中),雖然可以在b表的id字段建索引來(lái)提高查詢效率。但是并不能反過(guò)來(lái)拿著b表的數(shù)據(jù)到a表查,exists子查詢的查詢順序是固定死的。
為什么要反過(guò)來(lái)?因?yàn)槭紫瓤梢钥隙ǖ氖欠催^(guò)來(lái)的結(jié)果也是一樣的。這樣就又引出了一個(gè)更細(xì)致的疑問(wèn):在雙方兩個(gè)表的id字段上都建有索引時(shí),到底是a表查b表的效率高,還是b表查a表的效率高?
該如何進(jìn)一步優(yōu)化?把查詢修改成inner join連接查詢:select * from a inner join b on a.id=b.id; (但是僅此還不夠,接著往下看)
為什么不用left join 和 right join?這時(shí)候表之間的連接的順序就被固定住了,
比如左連接就是必須先查左表全表掃描,然后一條一條的到另外表去查詢,右連接同理。仍然不是最好的選擇。
為什么使用inner join就可以?inner join中的兩張表,如: a inner join b,但實(shí)際執(zhí)行的順序是跟寫(xiě)法的順序沒(méi)有半毛錢(qián)關(guān)系的,最終執(zhí)行也可能會(huì)是b連接a,順序不是固定死的。如果on條件字段有索引的情況下,同樣可以使用上索引。
那我們又怎么能知道a和b什么樣的執(zhí)行順序效率更高?答:你不知道,我也不知道。誰(shuí)知道?mysql自己知道。讓mysql自己去判斷(查詢優(yōu)化器)。具體表的連接順序和使用索引情況,mysql查詢優(yōu)化器會(huì)對(duì)每種情況做出成本評(píng)估,最終選擇最優(yōu)的那個(gè)做為執(zhí)行計(jì)劃。
在inner join的連接中,mysql會(huì)自己評(píng)估使用a表查b表的效率高還是b表查a表高,如果兩個(gè)表都建有索引的情況下,mysql同樣會(huì)評(píng)估使用a表?xiàng)l件字段上的索引效率高還是b表的。
而我們要做的就是:把兩個(gè)表的連接條件的兩個(gè)字段都各自建立上索引,然后explain 一下,查看執(zhí)行計(jì)劃,看mysql到底利用了哪個(gè)索引,最后再把沒(méi)有使用索引的表的字段索引給去掉就行了。