表連接的優化(五)
---
title: 不懂SQL優化?那你就OUT了(五)
MySQL如何優化--表連接
date: 2018-11-24
categories: 數據庫優化
---
上一遍我們討論了where 子句的優化,這一遍我們來討論一下表連接的優化
我們知道在數據庫中表連接有兩種方式:
1. 內連接(inner join)
2. 外連接(左外連接(left join), 右外連接(right join),全外連接(full join-->mysql 不支持全外連接)
### 什麽是表連接
表連接就是指將多個表聯合在一起實現查詢。
###表連接的原理
表連接采用的是<font style="color:coral">笛卡爾乘積</font>,稱之為橫向連接.
### 笛卡爾乘積
笛卡爾乘積:是指將多張表的所有數據相連,最後連接的結果數為多張表數量的乘積。
例如:這裏有一張學生信息表(3條數據) 和 一張班級表(4條數據)
表連接是采用笛卡爾乘積的原理,所以兩張表相連的結果 應該為 3 * 4 等於 12 條數據,
###表連接的語法:
select 列名列表 from 表1 (inner | left | right )join 表2 on 過濾條件 (inner|left|right)join 表3 on 過濾條件... ###內連接和外連接的區別
內連接:
內連接被稱為普通連接或者自然連接,內連接是從結果表中刪除與其他被連接表中沒有匹配行的所有行。
(註意:內連接可能會丟失信息)
如圖:
左外連接:
左外連接的結果集包括 left outer join子句中指定的左表的所有行,而不僅僅是連接列所匹配的行。 如果左表的某行在右表中沒有匹配行,則在相關聯的結果集行中右表的所有選擇列表列均為空值。
註:
寫在 左邊的表 叫做 左表
寫在 右邊的表 叫做 右表
如圖:
右外連接:
右外連接是左外連接的反向連接,將返回右表的所有行。 如果右表的某行在左表中沒有匹配行,則將為左表返回空值.
如圖:
全外連接:(mysql 不支持)
全外連接返回左表和右表中的所有行。當某行在另一個表中沒有匹配行時,則另一個表的選擇列表 列 包含空值。如果表之間有匹配行,則整個結果集包含基表的數據值。
如圖:
###MySQL的JOIN實現原理
> 在MySQL中,只有一種Join算法,就是大名鼎鼎的Nested Loop Join(嵌套循環連接),他沒有其他很多數據庫所提供的Hash Join,也沒有Sort Merge Join。顧名思義,Nested Loop Join 實際上就是通過驅動表的結果集作為循環基礎數據,然後一條一條的通過該結果集中的數據作為過濾條件到下一個表中查詢數據,然後合並結果。如果還有第三個參與Join,則再通過前兩個表的Join 結果集作為循環基礎數據,再一次通過循環查詢條件到第三個表中查詢數據,如此往復。
>
>
> —— 摘自《MySQL 性能調優與架構設計》
## 如何優化表連接
1.經常在連接的列上,也是外鍵上創建索引,可以加快連接的速度;
例如:兩張表,學生表和班級表
學生表中有 8388608 條數據
班級表中有 4條數據
未加索引時:
從結果看:
執行時間為: 7.43sec
對學生表進行了全表掃描(需要優化),掃描數據:8389023條
加上索引以後:
從結果看:
執行時間為: 4.56sec(效率提高了 2.87 s)
對學生表的也使用了索引,由全表掃描變成了ref,並且掃描數量為:41945, 查詢效率明顯提高了很多。
2. 驅動表的選擇非常重要,驅動表的數據小可以顯著降低掃描的行數,條件中盡量能夠過濾一些行將驅動表變得小一點,用小表去驅動大表。
一般情況下參與聯合查詢的兩張表都會一大一小,如果是內連接(join),在沒有其他過濾條件的情況下MySQL會選擇<font style="color:coral">小表(數據少的表)</font>作為驅動表, 如果是左外連接(left join)mysql會選擇左邊的表為驅動表,如果是右外連接(right join)則以右邊的表作為驅動表。
如圖1:(內連接)
從圖1可以看出,mysql在做聯合查詢的時候,首先掃描的班級表,會自動選擇小表(班級表(4條數據)為驅動表, 掃描班級表4條數據,然後在掃描學生表 41945 條數據。
如圖2:(左外連接)
從圖2可以看出,mysql在做左外連接查詢的時候,會選擇左邊的表(學生表(8388608條數據)為驅動表。
首先掃描學生表 8389023條數據,再掃描班級班表 4 條數據。
如圖3(右外連接)
從圖3可以看出,mysql在做又外連接查詢的時候,會選擇右邊的表(班級表)為驅動表。
首先掃描班級表 4 條數據,再掃描學生表 41945 條數據。
MySQL在執行join時會把join分為system/const/eq_ref/ref/range/index/ALL等好幾類,連接的效率從前往後依次遞減。
3.left join/right join和inner join時,盡量用inner join避免外聯結和null,如果where條件中含有右表的非空條件(除開is null),則left join語句等同於join語句,可直接改寫成join語句。
如圖4:(left join)
從結果中看出,首先掃描的學生表,再是班級表,最後是教師表。
如果where條件中含有右表的非空條件,則left join語句等同於join語句,可直接改寫成join語句。
如圖5:
從上面的結果看,兩句sql語句的執行計劃是一樣的。
4.在mysql的 left join 中條件放在 on 後面和在 where 後面是不同的
ON 條件(“A LEFT JOIN B ON 條件表達式”中的ON)用來決定如何從 B 表中檢索數據行。
如果 B 表中沒有任何一行數據匹配 ON 的條件,將會額外生成一行所有列為 NULL 的數據。
在連接匹配階段 WHERE 子句的條件都不會被使用,<font style="color:coral">僅在匹配階段完成以後,WHERE 子句條件才會被使用</font>。
ON/And: 使用And時,不論如何,左邊主表都完全顯示,輔表顯示符合ON條件的那條數據,其他行輔表數據都為NULL
關鍵字 Where 是針對 所有表連接匹配數據以後,where條件將會從滿足on 匹配條件的數據中再檢索一次。
**條件寫在不同的位置,得到的結果可能會不同,這一點要註意。**
例如:
**條件寫 on 後面 (由於數據較多,我只截取前10條數據)**
顯示左表所有的數據,在班級表中沒有匹配的,都顯示為null;
**條件寫 where 後面 (由於數據較多,我只截取前10條數據)**
沒有顯示學生表中所有的數據,只是顯示班級一的學生的信息,條件寫在where後面,是在表連接匹配數據後,在進行了一次數據的檢索。
On和Where都是作為條件約束來過濾數據,我們應該盡量在on過濾條件是盡可能多的匹配滿足條件,然後減少where子句的檢索。
表連接的優化(五)