在執行計劃中看到 `Bookmark Lookup` (SQL 2005+) 或 `Key Lookup` (SQL 2008+),代表非叢集索引查找後,仍需回叢集索引/堆積表取回非索引欄位。這是效能殺手之一,理解其機制是優化關鍵。
若查詢所需欄位**全部包含在非叢集索引鍵值 + 叢集鍵 (若叢集表) 中**,則**不需書簽查找**,直接從索引葉層返回,稱為「索引覆蓋」。實現方式:
| 場景 | 效能排序 (最好 → 最差) |
|---|---|
| 回傳列較多 | 索引覆蓋 > 叢集索引掃描 > 表掃描 > 堆積非叢集索引 > 叢集非叢集索引 |
| 回傳列較少 | 索引覆蓋 = 叢集索引尋找 > 堆積非叢集索引 > 叢集非叢集索引 > 表掃描 |
關鍵洞見:**叢集表上的非叢集索引查找比堆積表更貴**,因書簽為叢集鍵,需二次 B 樹尋找 (Key Lookup);堆積表書簽為 RID,直接定位頁面。但「索引覆蓋」可完全消除此差異。
Index Allocation Map,追蹤表/索引使用的配置單元 (Extent),`DBCC IND` 可見。
初讀《寫有效率的 SQL 查詢 (V)》如閱天書。隨經驗積累,重讀每有新得。「叢集表非叢集索引效能較差」並非絕對,關鍵在於**是否形成覆蓋**。持續閱讀經典、結合實戰驗證,是技術成長唯一捷徑。
IBatis.NET 使用 `Map` 導致動態 SQL 長度不一,`sp_executesql` 參數型別推導為 `NVARCHAR(n)` (n=實際長度),造成同樣邏輯產生大量計畫快取條目 (如 `NVARCHAR(6)` vs `NVARCHAR(7)`)。解決:明確指定參數長度/型別,或改用預存程序強制參數化。SQL 2005+ 引入 `OPTIMIZE FOR AD HOC WORKLOADS` 緩解單次計畫佔用,但根治仍在參數化規範。