延續上篇,當我們確保了資料存取的效率(消滅了不必要的 Scan 與 Key Lookup)後,接下來要面對的就是關聯式資料庫最核心的動作:將多張資料表結合在一起(Join)。
當你在 SQL 語法中寫下 INNER JOIN 或 LEFT JOIN 時,SQL Server 的 Query Optimizer(查詢最佳化程式)會根據資料表的大小、有沒有索引、以及資料是否已經排序,自動從武器庫中挑選最適合的實體運算子。在執行計畫中,你一定會遇到以下這三位主角:
精緻的雙層迴圈:Nested Loops (巢狀迴圈)

- 運作原理: 概念上就像是寫程式時的兩層 for 迴圈。SQL Server 會將資料量較小的表作為外部表(Outer Table),針對外部表的「每一列」,逐一去掃描內部表(Inner Table)尋找匹配的資料。
- 適合情境:小表 Join 大表,且大表的 Join 欄位上有索引。當內部表有索引時,SQL Server 可以直接使用高效的 Index Seek 來尋找目標,效能極好。
- 效能警訊: 如果兩張表都很大,且內部表沒有索引(被迫變成 Table Scan),那麼時間複雜度
O(N X M)會讓效能急速惡化。
暴力卻有效的碰撞:Hash Match (雜湊比對)

- 運作原理: 處理過程分為兩個階段。
- Build(建立)階段:掃描較小的表,在記憶體中建立一個 Hash Table(雜湊表)。
- Probe(探測)階段:逐一掃描較大的表,將每一列資料套用雜湊函數,去 Hash Table 中快速查詢並配對。
- 適合情境:兩張表都很大,且沒有合適索引的場合。這時 Query Optimizer 通常會自動選擇 Hash Match 作為最後防線。它的時間複雜度為
O(N + M),在處理無索引大數據時效能相對穩定。 - 效能警訊: 最大的致命傷是「記憶體消耗」,如果記憶體不足以容納整個 Hash Table,就會發生 Spill to TempDB(溢出到硬碟) 的現象(圖示上會出現黃色驚嘆號Warning),此時讀寫速度會大幅下降,是必須優先解決的瓶頸。
完美排序的雙劍合璧:Merge Join (合併連接)

- 運作原理: 就像拉鍊一樣的結合方式。前提是兩個輸入資料集都必須已經依照 Join Key 「排序完成」。執行時,SQL Server 會同時推進兩個指標,依序往下比對,兩邊的資料都只需要掃描一遍即可完成配對。
- 適合情境:兩表已有排序順序(如有索引,或前置步驟已排序),且資料量大的場合。這是記憶體需求極低、效能非常優異的演算法。
- 效能警訊: 若資料本身未經排序,SQL Server 為了硬湊出 Merge Join,必須先在前方加入一個昂貴的 Sort(排序) 運算子。這個額外付出的排序成本,往往會抵銷掉 Merge Join 帶來的所有優勢,得不償失。
優化建議
面對這三種 Join,我們該如何除錯與優化呢?
- 相信最佳化程式,但要學會抓漏:SQL Server 的 Query Optimizer 通常會自動做出最佳選擇。但在檢視執行計畫時,若你發現出現了 Hash Match 且帶有黃色警告標誌(Spill to TempDB),這就是強烈的優化訊號,此時應優先考慮建立適當的索引,或者調整查詢邏輯(例如縮小
WHERE條件的範圍)。 - 注意統計資料(Statistics)是否過時:Optimizer 依靠統計資料來決定派誰出場,如果它誤判了資料量,可能會在大數據上錯誤地選擇 Nested Loops。如果發現執行計畫中的 Actual Rows(實際資料列) 與 Estimated Rows(估計資料列) 差距懸殊,請盡快執行
UPDATE STATISTICS。 - 使用 Query Hints 強制指定的時機:在開發或除錯階段,你可以使用查詢提示如
OPTION (LOOP JOIN)、OPTION (HASH JOIN)或OPTION (MERGE JOIN)來強制 SQL Server 改變演算法,藉此比較不同執行計畫的效能差異,但建議在正式上線的程式碼中,盡量避免硬寫入這些 Hint,應讓資料庫保有隨未來資料量增長而自動切換演算法的彈性。
看懂了資料是怎麼拿的(Seek/Scan),也看懂了資料是怎麼合的(Join),接下來,我們要把焦點轉向執行計畫中引人注目但也是常被忽略的「警告標誌」,下一篇將剖析那些帶著黃色驚嘆號的節點,看懂資料庫發出的求救信號。













