生產環境中 SQL 效能問題多源於缺乏索引策略與不當查寫寫法。本文針對三大常見場景提供具體優化方案與原理分析。
SELECT TOP 100 * FROM 大表 ORDER BY Score DESC;
tempdb 對全表排序後取前 100 筆,極度消耗 CPU/IO/記憶體,甚至導致 tempdb 記錄檔暴漲。在 Score 欄位建立 **非叢集索引** (或包含查詢欄位的覆蓋索引):
CREATE INDEX IX_Score ON 大表 (Score DESC) INCLUDE (其他常查欄位);
Score 排序,引擎直接掃描索引前 100 筆,再透過書籤查找 (Key Lookup) 取回完整列,避免全表排序。INCLUDE 涵蓋所有 SELECT 欄位,形成 **覆蓋索引**,更可完全避免 Key Lookup,效能極致。WHERE CONVERT(CHAR(10), F_Time, 120) = CONVERT(CHAR(10), @DateTime, 120)
CONVERT) 導致 **SARGable** 失效,無法使用 F_Time 上的索引,只能全表掃描。DECLARE @Start DATETIME, @End DATETIME, @DateTime DATETIME;
SET @DateTime = GETDATE();
SET @Start = CONVERT(CHAR(10), @DateTime, 120); -- 當天 00:00:00
SET @End = DATEADD(SECOND, -1, DATEADD(DAY, 1, @Start)); -- 當天 23:59:59
SELECT * FROM 表 WHERE F_Time BETWEEN @Start AND @End;
F_Time 維持「裸露」狀態,可完美使用索引 Seek。Northwind Orders 表,EmployeeID 有索引。
SELECT EmployeeID, COUNT(*) FROM Orders GROUP BY EmployeeID;
→ 執行計畫顯示 **Index Scan/Seek + Stream Aggregate**,極快。
SELECT EmployeeID, SUM(Freight) FROM Orders GROUP BY EmployeeID;
→ 執行計畫顯示 **Table Scan + Hash Aggregate**,因 Freight 不在 EmployeeID 索引中,書籤查找成本過高,優化器選擇全表掃描。
CREATE INDEX IX_EmployeeID_Freight ON Orders (EmployeeID, Freight);
再次執行查詢 B,執行計畫改為 **Index Scan + Stream Aggregate**,僅掃描索引即可完成彙總,速度大幅提升。
| 場景 | 關鍵點 | 索引策略 |
|---|---|---|
| TOP N 排序 | 避免 tempdb 排序 | 排序欄位建索引 + INCLUDE 覆蓋 |
| 日期篩選 | 避免函數包裹欄位 | 日期欄位建索引 + 範圍查詢 (BETWEEN/>= <) |
| GROUP BY 彙總 | 彙總欄位需在索引中 | 分組欄位為索引鍵值前綴 + 彙總欄位 INCLUDE |
SET STATISTICS IO, TIME ON 與圖形化執行計畫,確認 Index Seek vs Scan、Lookup 成本。UPDATE STATISTICS 或啟用自動更新。