首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server SARGable 查詢寫法:避免索引列運算導致掃描的性能鐵律

     編輯:關於SqlServer

核心問題

對已建索引的列直接套用函數/運算,會導致優化器放棄 Index Seek 轉為 Index Scan,效能隨資料量線性惡化。

反面教材

-- 年月函數包裹索引列 → 全索引掃描
SELECT * FROM Sales.SalesOrderHeader
WHERE YEAR(OrderDate) = 2005 AND MONTH(OrderDate) = 7

雖有 Non-Clustered Index on OrderDate,優化器仍選擇 Scan。

正確寫法:SARGable (Search ARGument Able)

-- 等價範圍查詢 → Index Seek + Range Scan
SELECT * FROM Sales.SalesOrderHeader
WHERE OrderDate >= '20050701' AND OrderDate < '20050801'

原則:函數/運算移至常數側,保持索引列「乾淨」。

更多反模式與修正

反模式 (導致 Scan)修正模式 (Seek 友善)說明
CAST(CreditCardID AS CHAR(4)) = '1347'CreditCardID = CAST('1347' AS INT)轉型移至常數
ISNULL(Col, 0) = 5Col = 5 OR Col IS NULL展開邏輯
Col + 1 = 10Col = 9代數變換
SUBSTRING(Name, 1, 3) = 'ABC'Name LIKE 'ABC%'前綴匹配可 Seek
DATEDIFF(day, CreateDate, GETDATE()) < 7CreateDate > DATEADD(day, -7, GETDATE())時間運算移常數側

原理深度解析

  1. 索引結構: B+樹按原始列值有序儲存。
  2. 函數破壞有序性: f(Col) 的值分布與 Col 原值無單調對應關係,優化器無法導航 B+樹。
  3. 統計資訊失效: 統計資訊描述原始列分布,不適用 f(Col)。
  4. 結果: 唯一路徑是掃描所有葉節點逐行計算 f(Col) 再比較 —— 全索引掃描。

例外與邊界

  • 計算欄 + 索引: 建立持久化計算欄 Col_Computed AS (YEAR(OrderDate)) PERSISTED 再建索引,查詢直接用計算欄可 Seek。
  • 隱式轉換: WHERE VarcharCol = 123 觸發隱式轉換 CONVERT(INT, VarcharCol),同樣導致 Scan。嚴禁隱式轉換。
  • 參數嗅探: 動態 SQL/預存程序參數可能導致計劃不穩,配合 OPTION (RECOMPILE) 或 OPTIMIZE FOR 處理。

檢查清單

  • [ ] WHERE/JOIN/ON 條件中,索引列是否裸露(無函數/運算/隱式轉換)?
  • [ ] 範圍查詢是否用 >= 且 < 而非 BETWEEN(避免邊界歧義)?
  • [ ] LIKE 模式是否前綴固定('ABC%' 可 Seek,'%ABC'/'%ABC%' 必 Scan)?
  • [ ] 統計資訊是否最新(UPDATE STATISTICS 或自動更新)?

總結

「索引列不加工」是 SQL 效能調優的第一鐵律。將運算下推至常數側,讓優化器發揮 B+樹導航優勢,是從 Scan 到 Seek 的關鍵一步。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved