1.left、right、inner join 的區別
創建表t1、t2
CREATE TABLE `t2` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `a` (`a`) ) ENGINE=InnoDB; create table t1 like t2;
t1、t2分別插入5條數據
delimiter ;; create procedure idata() begin declare i int; set i=1; while(i<=5)do insert into t1 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata(); insert into t2 select * from t1 where a <= 4; insert into t2 values(6, 6, 6);
left join 會查詢出左表所有的數據,以及右表能連接上的字段
select * from t1 left join t2 on t1.id = t2.id;
right join 會查詢出右表所有的數據,以及左表能連接上的字段
select * from t1 right join t2 on t1.id = t2.id;
select * from t1 inner join t2 on t1.id = t2.id;
2.連接查詢原理
為了后面結果更為清晰,往t1再插入15條數據:
drop procedure idata; delimiter ;; create procedure idata() begin declare i int; set i=6; while(i<=20)do insert into t1 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata();
如果直接使用join語句,MySQL優化器可能會選擇表t1或t2作為驅動表,會影響分析SQL語句的執行過程。所以使用straight_join讓MySQL使用固定的連接方式執行查詢,這樣優化器只會按照指定的方式去join。
走索引的情況:
通過a字段連接查詢:
explain select * from t2 straight_join t1 on t1.a = t2.a;
t2作為驅動表,t1作為被驅動表,這條語句的explain結果:
因為被驅動表t1的字段a有索引,join過程用上了這個索引,因此這個語句的執行流程是這樣的:
-
從表t2中讀入一行數據 R;
-
從數據行R中,取出a字段到表t1里去查找;
-
取出表t1中滿足條件的行,跟R組成一行,作為結果集的一部分;
-
重復執行步驟1到3,直到表t2的末尾循環結束。
這個過程稱之為Index Nested-Loop Join
在這個流程中,掃描t2表5行、之后根據t2.a去表t1中查找,走的是樹搜索過程、因此每次掃描一行,總掃描行數為10行。
不走索引的情況
我們再用b字段關聯查詢
explain select * from t2 straight_join t1 on t1.b = t2.b;
explain結果如下:
由於表t1的字段b上沒有索引,所以每次都要把t1表中的每一行,拿出來進行對比, mysql采用的是Block Nested-Loop Join
,Extra可以看到。該算法並沒有將表t1全表掃描20次,它的執行流程為:
-
把表t2的數據讀入線程內存join_buffer中,由於這個語句中寫的是select *,因此是把整個表t2放入了內存;
-
掃描表t1,把表t1中的每一行取出來,跟join_buffer中的數據做對比,滿足join條件的,作為結果集的一部分返回。
在這個過程中,對表t1和t2都做了一次全表掃描,因此總掃描次數為25,由於join_buffer是以無序數組的方式組織的,因此對表t1中的每一行,都要做20次判斷,總共需要在內存中做的判斷次數是:20*5=100次。
因為使用到了join_buffer,而join_buffer大小是有限的,由join_buffer_size
設定,默認為256k。如果一次放不下t2中的所有字段,就會采用分段放的策略,執行策略如下:
-
假如t2表足夠大,掃描表t2,順序讀取數據行放入join_buffer中,放完第n行join_buffer滿了,繼續第2步;
-
掃描表t1,把t1中的每一行取出來,跟join_buffer中的數據做對比,滿足join條件的,作為結果集的一部分返回;
-
清空join_buffer;
-
繼續掃描表t2,順序讀取最后的m行數據放入join_buffer中,繼續執行第2步。
因此表t1可能會被掃描多次,在決定哪個表做驅動表的時候,應該是兩個表按照各自的條件過濾,過濾完成之后,計算參與join的各個字段的總數據量,數據量小的那個表,就是“小表”,應該作為驅動表。
三個結論:
-
如果可以使用被驅動表的索引,join語句還是有其優勢的;
-
不能使用被驅動表的索引,只能使用Block Nested-Loop Join算法,這樣的語句就盡量不要使用;
-
在使用join的時候,應該讓小表做驅動表。