在上一篇中,建立了閱讀執行計畫的「方向感」,學會透過箭頭粗細找出塞車路段;順著箭頭一路往右追溯到最源頭,就會看到 SQL Server 是如何進入資料庫「拿資料」,這一步至關重要,因為資料庫系統最大的效能瓶頸往往在於磁碟 I/O(資料讀寫),是在「精準尋找」還是在「盲目翻找」,決定了這個查詢是只要 0.1 秒,還是要跑 10 分鐘。
在執行計畫的最右側,最常看見的資料存取運算子有這幾種:
理想的境界:Index Seek
- 白話比喻: 就像查字典時,利用部首或注音索引,直接翻到你要的那一頁。
- 背後意義: 這是效能最好的存取方式,代表 SQL Server 完美利用了 B-Tree 索引結構,精確定位到符合
WHERE條件的資料列;看到這個圖示通常代表你的索引設計順利發揮了作用。
尷尬的中間地帶:Index Scan
- 白話比喻:不用翻整本字典,但把字典的「整個附錄/整個目錄」從頭到尾看了一遍。
- 背後意義:看到 “Index” 這個字或許會覺得令人安心,但請注意後面的 “Scan”;這代表查詢條件無法讓 SQL Server 縮小搜尋範圍,只好把整個索引從頭到尾掃了一遍。
- 優化建議:
- 改善查詢條件(避免索引失效):這是最常見的「索引殺手」,例如在欄位上套函數(如
WHERE YEAR(created_at) = 2026),或發生型別隱含轉換(字串比對數字欄位),可以改成對索引友善的寫法,例如:WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01',讓 SQL Server 能切換回 Index Seek。 - 建立或調整 Composite Index(複合索引):把常常一起出現在
WHERE、ORDER BY的欄位組合進同一個索引,且要注意欄位順序。 - 減少回傳欄位:盡量避免
SELECT *,只取真正需要的欄位,有助於讓 Optimizer 選擇更精準的索引。
- 改善查詢條件(避免索引失效):這是最常見的「索引殺手」,例如在欄位上套函數(如
效能的紅燈:Table Scan / Clustered Index Scan
- 白話比喻:為了找書裡的一句話,從第一頁逐字逐句讀到最後一頁。
- 背後意義:這是最需要優先處理的狀況,代表完全沒有可用的索引,或查詢條件根本繞過了索引;SQL Server 必須把整張表掃描一遍,一筆一筆核對。
- 優化建議:
- 建立索引:最直接的解法,可以先參考 Execution Plan 上方黃色字體的 Missing Index 提示來建立 Non-Clustered Index,這通常能立竿見影。
- 檢查資料選擇性(Selectivity):如果你的
WHERE條件篩出來的資料佔了整張表的 20%–30% 以上,SQL Server 會認為「既然都要抓這麼多資料,不如直接掃整張表比較快」;此時加索引沒用,需要檢討的是查詢邏輯本身。 - 更新統計資料:執行
UPDATE STATISTICS或sp_updatestats,有時候是資料庫的統計資訊太舊,導致 Optimizer 誤判,放著好好的索引不用跑去掃表。 - 檢查
NOLOCK或 Hint 干擾:確認程式碼中是否有不必要的查詢提示(Hint)強制改變了 SQL Server 的預設行為。
隱藏的效能殺手:Key Lookup
白話比喻:用目錄找到了頁碼,但發現那頁只有大綱,還得跑去另一本詳細版的手冊裡翻出完整內容。
背後意義:有時候明明看到計畫裡有 Index Seek,查詢卻還是很慢?原因是現有索引只包含部分欄位;SQL Server 找到條件相符的資料列後,還要再「回表(回 Clustered Index)」去撈
SELECT中缺乏的其他欄位,造成龐大的額外 I/O。優化建議:
- 加上
INCLUDE欄位(涵蓋索引 Covering Index):這是最常見且有效的解法;把SELECT清單中非索引的欄位,加到 Non-Clustered Index 的INCLUDE裡,SQL Server 就不需要再回表了。
1-- 原本會造成 Key Lookup 的索引 2CREATE INDEX IX_Orders_CustomerId ON Orders (CustomerId); 3 4-- 優化:加上 INCLUDE,把常查的欄位一起放進去 5CREATE INDEX IX_Orders_CustomerId ON Orders (CustomerId) 6INCLUDE (OrderDate, TotalAmount, Status);- 減少
SELECT欄位:如果 Key Lookup 次數極高,最快的解法是拿掉不必要的SELECT欄位,減少需要回表的負擔。 - 合併重複的索引:如果同一張表有多個相似的索引,各自
INCLUDE不同的欄位,可以考慮合併成一個較完整的索引,降低維護成本與回表機率。
- 加上
搞定了資料讀取的第一關,手上就有了最精簡的資料集;但在關聯式資料庫中,經常需要把多張表的資料拼湊在一起;這時候,SQL Server 會派誰上場呢?