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

SQL Server 查詢優化核心:SARG 條件、索引策略與 SQL 寫法陷阱

     編輯:關於SqlServer

深度解析 SQL Server 查詢優化器與 SARG 機制

許多開發人員擔心 WHERE 條件順序影響效能,實則 SQL Server 擁有強大的「查詢分析優化器」,能自動計算搜尋條件並選擇最佳索引。但理解其運作原理(特別是 SARG 概念)對編寫高效 SQL 至關重要。

查詢優化器自動優化機制

優化器會分析 WHERE 子句每個階段,判斷是否可作為「掃描參數」(Search ARGument,SARG)。符合 SARG 形式的條件可利用索引快速縮小掃描範圍;不符合則需全表掃描逐行判斷。

SARG 定義與標準形式

SARG 形式:

欄位名 運算子 <常數 或 變數>
或
<常數 或 變數> 運算子 欄位名

欄位名與常數/變數分居運算子兩側。範例:

  • Name = '張三'
  • 價格 > 5000
  • 5000 < 價格
  • Name = '張三' AND 價格 > 5000

實戰 SARG 陷阱與對策

1. LIKE 通配符位置決定索引可用性

  • name LIKE '張%' → 屬 SARG,可用索引
  • name LIKE '%張' → 非 SARG,前置 % 導致索引失效,全表掃描

2. OR 導致全表掃描

  • Name = '張三' AND 價格 > 5000 → 符合 SARG
  • Name = '張三' OR 價格 > 5000 → 非 SARG,引發全表掃描

3. 非運算子與函數破壞 SARG

典型非 SARG 形式:

  • 否定運算子:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE
  • 函數運算:ABS(價格) < 5000、Name LIKE '%三'

特例:WHERE 價格 * 2 > 5000 優化器可轉換為 WHERE 價格 > 2500 視為 SARG,但不建議依賴此行為,因無法保證完全等價。

4. IN 等價於 OR,同樣導致全表掃描

SELECT * FROM table1 WHERE tid IN (2,3)
-- 等同於
SELECT * FROM table1 WHERE tid = 2 OR tid = 3

即使 tid 有索引也會失效。

5. 減少使用 NOT

6. EXISTS 與 IN 效能相同

許多資料稱 EXISTS 優於 IN,但實測(使用 pubs 資料庫,開啟 STATISTICS IO):

-- 使用 IN
SELECT title, price FROM titles 
WHERE title_id IN (SELECT title_id FROM sales WHERE qty > 30)
-- 掃描計數 18,邏輯讀 56

-- 使用 EXISTS
SELECT title, price FROM titles 
WHERE EXISTS (SELECT * FROM sales WHERE sales.title_id = titles.title_id AND qty > 30)
-- 掃描計數 18,邏輯讀 56

兩者 I/O 統計完全一致,效能無差異。NOT EXISTS 與 NOT IN 同理。

7. CHARINDEX() 與前置 % LIKE 效能相同

-- CHARINDEX 版本
SELECT gid, title, fariqi, reader FROM tgongwen 
WHERE CHARINDEX('刑偵支隊', reader) > 0 AND fariqi > '2004-5-5'
-- 7秒,掃描計數 4,邏輯讀 7155

-- LIKE 前置% 版本
SELECT gid, title, fariqi, reader FROM tgongwen 
WHERE reader LIKE '%' + '刑偵支隊' + '%' AND fariqi > '2004-5-5'
-- 7秒,掃描計數 4,邏輯讀 7155

無效能優勢,CHARINDEX 甚至較難閱讀。

8. UNION 不絕對優於 OR

一般建議用 UNION 替代 OR,大部分情況有效:

-- OR 版本:68秒,邏輯讀 404008
SELECT * FROM Tgongwen WHERE fariqi = '2004-9-16' OR gid > 9990000

-- UNION 版本:9秒,邏輯讀 67489
SELECT * FROM Tgongwen WHERE fariqi = '2004-9-16'
UNION
SELECT * FROM Tgongwen WHERE gid > 9990000

但當 OR 兩側查詢欄位相同時,UNION 反而較慢:

-- OR 版本:6423毫秒,掃描計數 2
SELECT * FROM Tgongwen WHERE fariqi = '2004-9-16' OR fariqi = '2004-2-5'

-- UNION 版本:11640毫秒,掃描計數 8
SELECT * FROM Tgongwen WHERE fariqi = '2004-9-16'
UNION
SELECT * FROM Tgongwen WHERE fariqi = '2004-2-5'

原因:UNION 需合併去重,增加額外開銷。

9. 欄位提取遵循「需多少、取多少」原則

SELECT TOP 10000 gid, fariqi, reader, title FROM tgongwen ORDER BY gid DESC  -- 4673ms
SELECT TOP 10000 gid, fariqi, title FROM tgongwen ORDER BY gid DESC           -- 1376ms
SELECT TOP 10000 gid, fariqi FROM tgongwen ORDER BY gid DESC                  -- 80ms

每減少一個欄位,速度顯著提升,幅度取決於欄位大小。

10. COUNT(*) 不比 COUNT(欄位) 慢

SELECT COUNT(*) FROM Tgongwen      -- 1500ms
SELECT COUNT(gid) FROM Tgongwen    -- 1483ms (主鍵)
SELECT COUNT(fariqi) FROM Tgongwen -- 3140ms
SELECT COUNT(title) FROM Tgongwen  -- 52050ms

COUNT(*) 與 COUNT(主鍵) 速度相當,且比其他欄位快。推測引擎自動選擇最小欄位統計。明確寫 COUNT(主鍵) 更直接。

11. ORDER BY 聚集索引欄位效能最高

-- 無 ORDER BY(fariqi 為聚集索引):196ms
SELECT TOP 10000 gid, fariqi, reader, title FROM tgongwen

-- ORDER BY gid(主鍵非聚集):4720ms
SELECT TOP 10000 gid, fariqi, reader, title FROM tgongwen ORDER BY gid ASC

-- ORDER BY gid DESC:4736ms
SELECT TOP 10000 gid, fariqi, reader, title FROM tgongwen ORDER BY gid DESC

聚集索引欄位排序快 3/10。小數據量差異明顯,大數據量(10萬+)差異縮小。

日期欄位精度對查詢速度的影響

測試 100 萬筆資料:2004/1/1 後 50 萬筆(僅 2 個不同日期,精確到日);之前 50 萬筆(5000 個不同日期,精確到秒)。

SELECT * FROM Tgongwen WHERE fariqi > '2004-1-1' ORDER BY fariqi  -- 6390ms
SELECT * FROM Tgongwen WHERE fariqi < '2004-1-1' ORDER BY fariqi  -- 6453ms

結論:日期欄位包含時分秒不會降低查詢速度。

索引建立黃金法則

  • 索引助查詢,但過多/不當導致系統低效(維護成本、索引碎片)
  • 建立「適當」索引體系,聚集索引選擇需精益求精
  • DBA 應多測試方案,找出最高效配置
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved