MySQL連接查詢區別及原理


在實操之中,對於join、left join、right join通常情況下也是僅僅用到了left join,對於其他幾種,心存疑惑,因此對幾種join查詢、以及原理做個記錄。

 

 

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;

inner join(等值連接) 只返回兩個表中聯結字段相等的行

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過程用上了這個索引,因此這個語句的執行流程是這樣的:

  1. 從表t2中讀入一行數據 R;

  2. 從數據行R中,取出a字段到表t1里去查找;

  3. 取出表t1中滿足條件的行,跟R組成一行,作為結果集的一部分;

  4. 重復執行步驟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次,它的執行流程為:

  1. 把表t2的數據讀入線程內存join_buffer中,由於這個語句中寫的是select *,因此是把整個表t2放入了內存;

  2. 掃描表t1,把表t1中的每一行取出來,跟join_buffer中的數據做對比,滿足join條件的,作為結果集的一部分返回。

在這個過程中,對表t1和t2都做了一次全表掃描,因此總掃描次數為25,由於join_buffer是以無序數組的方式組織的,因此對表t1中的每一行,都要做20次判斷,總共需要在內存中做的判斷次數是:20*5=100次。

因為使用到了join_buffer,而join_buffer大小是有限的,由join_buffer_size設定,默認為256k。如果一次放不下t2中的所有字段,就會采用分段放的策略,執行策略如下:

  1. 假如t2表足夠大,掃描表t2,順序讀取數據行放入join_buffer中,放完第n行join_buffer滿了,繼續第2步;

  2. 掃描表t1,把t1中的每一行取出來,跟join_buffer中的數據做對比,滿足join條件的,作為結果集的一部分返回;

  3. 清空join_buffer;

  4. 繼續掃描表t2,順序讀取最后的m行數據放入join_buffer中,繼續執行第2步。

因此表t1可能會被掃描多次,在決定哪個表做驅動表的時候,應該是兩個表按照各自的條件過濾,過濾完成之后,計算參與join的各個字段的總數據量數據量小的那個表,就是“小表”,應該作為驅動表

三個結論:

  1. 如果可以使用被驅動表的索引,join語句還是有其優勢的;

  2. 不能使用被驅動表的索引,只能使用Block Nested-Loop Join算法,這樣的語句就盡量不要使用;

  3. 在使用join的時候,應該讓小表做驅動表。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM