學了 JOIN 之後,你可能會好奇:JOIN 跟 WHERE 的執行順序是什麼?
例如:
SELECT 組別.名稱, 組員.名稱
FROM 組別
LEFT JOIN 組員 ON 組別.組別ID = 組員.組別ID
WHERE 組別.領域 = 'Web'這段 SQL 裡面有三件事:JOIN(合併)、ON(合併條件)、WHERE(篩選條件)。
問題來了:這三件事的執行順序是什麼?
是先做完 JOIN 的排列組合,再用 ON 和 WHERE 篩選?
還是先用 WHERE 把資料篩小,再做 JOIN?
這個順序會影響效能嗎?
JOIN 和 ON 的執行順序
先回顧一下,前面學到的執行順序:
- FROM — 決定從哪張表讀資料
- WHERE — 篩選資料
- SELECT — 選取欄位
JOIN 寫在 FROM 後面,所以它跟 FROM 是同一個階段。
加上 JOIN 之後,執行順序變成:
- FROM + JOIN + ON — 合併兩張表,用 ON 篩選配對
- WHERE — 篩選合併後的資料
- SELECT — 選取欄位
FROM 先做,這完全沒有懸念。
但 FROM 這個階段裡面,JOIN 和 ON 的順序是什麼?
前面的文章有提到,JOIN 的運作方式是:先把兩張表做排列組合,再用 ON 條件篩選。
所以在 FROM 這個階段裡,順序是:
- 先做排列組合(把兩張表的每一列互相配對)
- 再用 ON 條件篩選(留下符合條件的配對)
這很合理,因為你還沒合併之前,根本沒辦法做篩選。
合併完之後,才進到 WHERE 階段,再做一次篩選。
整個流程是:排列組合 → ON 篩選 → WHERE 篩選。
但這個順序有一個隱憂:效能。
排列組合的效能問題
如果真的按照上面的順序,資料庫要先做完整的排列組合,再來篩選。
想像一下:組別表有 10 萬筆資料,組員表也有 10 萬筆資料。
排列組合就是 10 萬 × 10 萬 = 100 億筆。
先做出 100 億筆配對,再慢慢篩選?
這樣也太浪費了。
如果能在合併之前,先把資料篩小,效能應該會好很多。
例如我們只需要 Web 領域的組別,組別表裡可能只有 2 筆是 Web。
2 × 10 萬 = 20 萬筆,跟 100 億比起來差非常多。
所以直覺上,應該「先篩選,再合併」會比較好。
那這兩種做法寫成 SQL 會長什麼樣子?我們用一個具體的例子來比較。
先併再篩 vs 先篩再併
題目:找出 Web 領域的組別,以及這些組別底下的組員。
組別表:
組員表:
先併再篩
先把兩張表合併,再用 WHERE 篩選:
SELECT 組別.名稱 AS 組別名稱, 組員.名稱 AS 組員名稱
FROM 組別
LEFT JOIN 組員 ON 組別.組別ID = 組員.組別ID
WHERE 組別.領域 = 'Web'照前面說的執行順序,資料庫會:
- 先做排列組合(4 × 5 = 20 筆配對)
- 用 ON 條件篩選(留下組別ID 相符的,剩 5 筆)
- 用 WHERE 篩選(留下領域是 Web 的,剩 3 筆)
最終結果:
先篩再併
先用子查詢把組別表篩小,再做合併:
SELECT 篩選後的組別.名稱 AS 組別名稱, 組員.名稱 AS 組員名稱
FROM (
SELECT * FROM 組別 WHERE 領域 = 'Web'
) AS 篩選後的組別
LEFT JOIN 組員 ON 篩選後的組別.組別ID = 組員.組別ID這種做法的執行順序是:
- 先執行子查詢,從組別表篩出 Web 領域的(剩 2 筆)
- 再做排列組合(2 × 5 = 10 筆配對)
- 用 ON 條件篩選(留下組別ID 相符的,剩 3 筆)
最終結果:
兩種寫法的結果完全一樣。
比較兩種寫法
從效能的角度來看,先篩再併的排列組合比較小,理論上比較快。
但實際上,你不需要刻意選哪一種。
資料庫會自動優化
你寫的 SQL,其實是在告訴資料庫「你想要什麼結果」,而不是「你要資料庫怎麼做」。
資料庫收到你的 SQL 之後,不會傻傻地照順序執行。
它內部有一個叫「查詢優化器」的機制,會在真正執行之前,先分析你的 SQL,找出最有效率的做法。
例如它看到 WHERE 組別.領域 = 'Web',會判斷:「這個條件只跟組別表有關,我可以先把組別表篩小,再來做 JOIN,這樣排列組合會少很多。」
所以不管你寫的是先併再篩,還是先篩再併,查詢優化器都會自動幫你調整成最有效率的順序。
也就是說,就算你寫了先併再篩:
SELECT 組別.名稱 AS 組別名稱, 組員.名稱 AS 組員名稱
FROM 組別
LEFT JOIN 組員 ON 組別.組別ID = 組員.組別ID
WHERE 組別.領域 = 'Web'資料庫在執行的時候,會自動把 組別.領域 = 'Web' 提前做,把組別表從 4 筆縮小到 2 筆,再來做 JOIN。
效果跟你手動寫子查詢是一樣的。
如果你去看兩種寫法的執行計畫,會發現執行成本和執行時間幾乎一模一樣。
小結
這篇搞清楚了一件事:JOIN 和 WHERE 的執行順序不用擔心。
概念上的執行順序:
排列組合 → ON 篩選 → WHERE 篩選。
先合併,再篩選。
實際上的執行順序:
資料庫系統會自動優化,把能先做的篩選提前做,減少排列組合的大小。
結論:
不用刻意寫子查詢去「先篩再併」,因為資料庫已經會幫你做這件事。
怎麼方便就怎麼寫,怎麼好讀就怎麼寫。
大部分的情況下,先併再篩的寫法比較簡單乾淨,所以我們通常都用這種寫法。