本文結合生產環境實測數據,深度剖析 SQL Server 聚簇與非聚簇索引的選型誤區、查詢優化器與 SARG 機制、常見 SQL 寫法效能陷阱,並給出生產可用的高效分頁存儲過程方案。
核心差異: 聚簇索引擅長範圍查詢、排序;非聚簇索引適合點查詢、高區分度欄位。
| 場景 | 聚簇 | 非聚簇 |
|---|---|---|
| 頻繁 GROUP BY / ORDER BY | ✓ | ✓ |
| 範圍查詢(BETWEEN、>、<) | ✓ | ✗ |
| 極少不同值(如性別) | ✗ | ✗ |
| 少量不同值(如部門) | ✓ | ✗ |
| 大量不同值(如 ID、交易號) | ✗ | ✓ |
| 頻繁更新的欄位 | ✗ | ✓ |
| 外鍵 / 主鍵欄位 | ✓ | ✓ |
實測(1000 萬筆,近 3 月 25 萬筆):
結論: 自增 ID 無業務查詢價值,浪費寶貴的聚簇索引資源;應建立在「高頻範圍查詢、排序」欄位(如業務日期)。
同樣查詢、同樣欄位,聚簇與非聚簇索引效能差異達 20 倍。關鍵在於索引類型與查詢模式匹配。
實測複合聚簇 (日期, 用戶名):
鐵律: 複合索引前導欄必須為最高頻查詢欄位;僅用非前導欄查詢無法使用索引。
SARG (Search ARGument) 定義:欄位 運算子 常數/變數。符合 SARG 才能利用索引查找。
| 寫法 | SARG? | 說明 |
|---|---|---|
name LIKE '張%' | ✓ | 前綴匹配可用索引 |
name LIKE '%張' | ✗ | 前導通配符導致全表掃描 |
WHERE a=1 OR b=2 | ✗ | OR 通常引發全表掃描 |
WHERE a=1 AND b=2 | ✓ | AND 可結合索引 |
WHERE ABS(price)<5000 | ✗ | 函數/運算在欄位上破壞 SARG |
WHERE price*2>5000 | ✓* | 優化器可改寫,但不建議依賴 |
WHERE id IN (2,3) | ✗ | 等價 OR,引發全表掃描 |
EXISTS vs IN | 同效 | 實測邏輯讀、執行計畫完全一致 |
CHARINDEX() vs LIKE '%...%' | 同效 | 皆為全表掃描,無效能優勢 |
SELECT *:實測少抓一欄即提速,依欄位大小而定。COUNT(*) 不慢於 COUNT(col):主鍵計數最快,長文字欄位最慢。TOP 限制結果集:大數據分頁核心在減少 I/O,TOP 經優化器優化,遠優於游標法。UNION 不總優於 OR:同欄位 OR 改 UNION 反而變慢(掃描索引 vs 全表掃描的權衡)。| 頁碼 | 方案1:表變量+ROWCOUNT | 方案2:NOT IN 子查詢 | 方案3:MAX/MIN 锚點法(推薦) |
|---|---|---|---|
| 1 | 60ms | 30ms | 76ms |
| 100 | 1076ms | 720ms | 130ms |
| 1000 | 17110ms | 470ms | 250ms |
| 10000 | 24796ms | 4500ms | 140ms |
| 250000 | 28140ms | 128720ms | 2330ms |
方案 3 核心邏輯:
SELECT TOP @PageSize *
FROM table1
WHERE id > (
SELECT MAX(id) FROM (
SELECT TOP ((@PageIndex-1)*@PageSize) id FROM table1 ORDER BY id
) AS T
)
ORDER BY id
利用唯一遞增鍵的 MAX/MIN 作為分頁錨點,查詢符合 SARG,深翻頁效能幾乎不衰減。
pagination3支援:動態表名、欄位、排序、條件、總筆數統計、升/降序、首頁快速路徑。大數據尾頁查詢穩定在 9 秒內,避免逾時。
矛盾: 業務最需「日期範圍查詢」(適合聚簇)與「分頁排序需唯一鍵」(需唯一值)往往不同欄位。
解法: 新增 datetime2(3) 欄位(預設 GETDATE(),精確到毫秒)+ UNIQUE 約束 → 設為聚簇索引。兼顧:
MAX/MIN 分頁錨點優化後:分頁速度幾十毫秒,範圍查詢無性能損失。
SQL 效能調校無銀彈,核心在於:聚簇索引建在最高頻篩選/排序欄位、查詢寫法符合 SARG、分頁採用錨點法避免深翻頁效能崩塌。持續以 EXPLAIN/執行計畫驗證,在業務增長中動態調整索引策略,才能構建經得起時間考驗的高效能資料庫系統。