掌握MySQL連接查詢到底什麼是驅動表
準備我們需要的表結構和數據
兩張表 studnet(學生)表和score(成績)表, 創建表的SQL語句如下
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`no` varchar(20) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `score` (
`id` int(11) NOT NULL,
`no` varchar(20) DEFAULT NULL,
`chinese` double(4,0) DEFAULT NULL,
`math` double(4,0) DEFAULT NULL,
`engilsh` double(4,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
MySQL連接查詢分為以下三種
left join 左連接,用法如下,這種查詢會把左表(student)所有數據查詢出來,右表不存在的用空表示,結果圖如下
select * from student s1 left join score s2 on s1.on = s2. on
right join 右連接, 用法如下,這種查詢會把右表(score)所有數據查詢出來,左表不存在的用空表示,結果圖如下
select * from student s1 right join score s2 on s1.no = s2.no
inner join 內連接,用法如下,這種查詢會把左右表都存在的數據查詢出來,不存在數據忽略,結果圖如下
select * from student s1 inner join score s2 on s1.no = s2.no
連接查詢中需要注意的點
什麼是驅動表,什麼是被驅動表,這兩個概念在查詢中有時容易讓人搞混,有下面幾種情況,大家需要了解。
- 當連接查詢沒有where條件時,左連接查詢時,前面的表是驅動表,後面的表是被驅動表,右連接查詢時相反,內連接查詢時,哪張表的數據較少,哪張表就是驅動表
- 當連接查詢有where條件時,帶where條件的表是驅動表,否則是被驅動表
怎麼確定我們上面的兩種情況呢,執行計劃是不會騙人的,我們針對上面情況分別看看執行計劃給出的答案
首先第一種情況,student表中3條數據,score表中2條數據,但兩張表中只有一條數據是關聯的(編號是1),看如下SQL查詢
//左連接查詢
explain select * from student s1 left join score s2 on s1.no = s2.no
//右連接查詢
explain select * from student s1 right join score s2 on s1.no = s2.no
//內連接查詢
explain select * from student s1 inner join score s2 on s1.no = s2.no
執行計劃中靠前的表是驅動表,我們看下面三種圖中,是不是全度符合情況一,第一張圖中s1是驅動表,第二張圖中s2是驅動表,第三種途中s2是驅動表
其次第二種情況,還是上面三種SQL語句,我們分別加上where條件,再來看看執行計劃的結果是什麼樣呢?
//左連接查詢
explain select * from student s1 left join score s2 on s1.no = s2.no
where s2. no = 1
//右連接查詢
explain select * from student s1 right join score s2 on s1.no = s2.no
where s1.no = 1
//內連接查詢
explain select * from student s1 inner join score s2 on s1.no = s2.no
where s1.no = 1
我們看下面三種執行計劃結果,全都以where條件為準了,而且跟上面情況一的都相反了,因此情況二也是得到了驗證.
連接查詢優化
要理解連接查詢優化,得先理解連接查詢的演算法,連接查詢常用的一共有兩種演算法,我們簡要說明一下
Simple Nested-Loop Join Algorithms (簡單嵌套循環連接演算法)
比如上面的查詢中,我們確定了驅動表和被驅動表,那麼查詢過程如下,很簡單,就是雙重循環,從驅動表中循環獲取每一行數據,再在被驅動表匹配滿足條件的行。
for (row1 : 驅動表) {
for (row2 : 被驅動表){
if (conidtion == true){
send client
}
}
}
Index Nested-Loop Join Algorithms (索引嵌套循環連接演算法)
上面雙重for循環的查詢中,相信很多研發人員看到這種情況第一個想法就是性能問題,是的,join查詢的優化思路就是小表驅動大表,而且在大表上創建索引(也就是被動表創建索引),如果驅動表創建了索引,MySQL是不會使用的
for (row1 : 驅動表) {
索引在被驅動表中命中,不用再遍歷被驅動表了
}
Block Nested-Loop Join Algorithm(基於塊的連接嵌套循環演算法)
其實很簡單就是把一行變成了一批,塊嵌套循環(BNL)嵌套演算法使用對在外部循環中讀取的行進行緩衝,以減少必須讀取內部循環中的表的次數。例如,如果將10行讀入緩衝區並將緩衝區傳遞到下一個內部循環,則可以將內部循環中讀取的每一行與緩衝區中的所有10行進行比較。這將內部表必須讀取的次數減少了一個數量級。
MySQL連接緩衝區大小通過這個參數控制 : join_buffer_size
MySQL連接緩衝區有一些特徵,只有無法使用索引時才會使用連接緩衝區;聯接中只有感興趣的列存儲在其聯接緩衝區中,而不是整個行;為每個可以緩衝的連接分配一個緩衝區,因此可以使用多個連接緩衝區來處理給定查詢;在執行連接之前分配連接緩衝區,並在查詢完成後釋放連接緩衝區
所以查詢時最好不要把 * 作為查詢的欄位,而是需要什麼欄位查詢什麼欄位,這樣緩衝區能夠緩衝足夠多的行。
從上面的執行計劃中其實我們已經看到了 useing join buffer了,是的,那是因為我們對兩張表都有創建索引
三種演算法優先順序
第一種演算法忽略,MySQL不會採用這種的,當我們對被驅動表創建了索引,那麼MySQL一定使用的第二種演算法,當我們沒有創建索引或者對驅動表創建了索引,那麼MySQL一定使用第三種演算法
MySQL連接演算法官方文檔
//dev.mysql.com/doc/refman/8.0/en/nested-loop-joins.html