1. 關聯查詢執行流程
MySQL執行關聯查詢的策略很簡單,他會從一個表中循環取出單條數據,然后用該條數據到下一個表中尋找匹配的行,然后回溯到上一個表,到所有的數據匹配完成為止。因此也被稱為“嵌套循環關聯”。
來看下面這個SQL:
select tb1.col1, tb2,col2
from tb1 inner join tb2 using(col3)
where tb1.col1 in (5,6)
他的執行順序為(偽代碼):
List outerDataList = "select * from tb1 where col1 in (5,6)"
for(outerData in outerDataList){
List innerDataList = "select * from tb2 where col3 = outerData.col3"
for(innerData : innerDataList){
output(outterData,innerData)
}
}
MySQL認為所有的查詢都是一次關聯查詢,所以如果查詢一個表,上述過程也適合,不過只需要完成上面外層的基本操作。
再來看看left outter join
查詢的過程,SQL如下:
select tb1.col1, tb2,col2
from tb1 left outer join tb2 using(col3)
where tb1.col1 in (5,6)
偽代碼如下:
List outerDataList = "select * from tb1 where col1 in (5,6)"
for(outerData in outerDataList){
List innerDataList = "select * from tb2 where col3 = outerData.col3"
if(innerDataList != null){
for(innerData : innerDataList){
output(outterData,innerData)
}
}else{
// inner表無對應數據,以outter數據為准
output(outterData,null)
}
}
但是這種遍歷的查詢方式不能滿足所有的聯合查詢,比如“全外連接”查詢(full outer join)不能使用該方法來實現,這可能是MySQL不支持全外接查詢的原因 ~~~
2. 優化
MySQL會將查詢命令生成一顆指令樹,比如四表聯合查詢的指令樹如下:
MySQL在生成指令樹之前會先對SQL語句的執行效率進行評估,然后選擇他認為效率最高的關聯順序執行。對於如下SQL:
EXPLAIN SELECT
actor.NAME,
film.title
FROM
actor actor
INNER JOIN film_actor USING ( actor_id )
INNER JOIN film USING ( film_id )
從執行計划可以看出,MySQL選擇將film作為第一個關聯表,拿到數據后再依次掃描film_actor、actor表取數據。MySQL的選擇策略是,盡量讓查詢執行更少的嵌套循環和回溯操作,因此,他會盡量將外層查詢的數據量更少。因為film表只有4條記錄,actor表有6條記錄,因此他認為選擇將film作為第一個表開始查詢有更高的執行效率。
但是MySQL的優化策略會比這復雜的多,MySQL會計算所有執行順序的代價,然后選擇他認為的最佳執行計划。但是,如果聯合查詢的表比較多,他不一定能窮舉所有的執行情況選擇最佳的執行策略,所以這種默認的優化方式卻不一定總是最佳的。還是以上條SQL為例子,假設在film表的film_id字段上建立了索引,那么即使film上的字段少於actor,可能使用actor表作為第一個表進行查詢,效率會更高(里層嵌套查詢film表數據時可以使用索引)。如果你認為有更佳的執行順序,可以使用STRAIGHT_JOIN
關鍵字強行執行查詢順序:
EXPLAIN SELECT
actor.NAME,
film.title
FROM
actor actor
STRAIGHT_JOIN film_actor USING ( actor_id )
STRAIGHT_JOIN film USING ( film_id )
注意:絕大多數時候,MySQL做出的判斷都比人類要准確,絕大多數時候,不推薦強制執行順序。