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

SQL Server 查詢優化器深度解析:SARG 條件、索引失效陷阱與高效寫法實戰

     編輯:關於SqlServer

前言:SQL 執行順序不等於寫作順序

許多開發者擔心 WHERE name='zhangsan' AND tID > 10000 與條件順序互換會影響效能。實則 SQL Server 查詢優化器 會自動分析 WHERE 子句,選擇最佳索引縮小掃描範圍,條件順序不影響執行計畫。但理解優化器運作原理,才能寫出真正可優化的 SQL。

核心概念:SARG (Search ARGument) 可優化條件

優化器在查詢分析階段判斷條件是否可作為「掃描參數」(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,全表掃描

改寫建議:用 UNION ALL 分拆(見第 8 點)。

3. 否定運算符與函數殺死索引

典型非 SARG:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE 及欄位上使用函數:

  • ABS(價格) < 5000 ❌
  • Name LIKE '%三' ❌

特例:價格*2 > 5000 優化器可轉為 價格 > 2500,但不建議依賴隱式轉換,可能不等價。

4. IN 等價於 OR,同樣失效

SELECT * FROM table1 WHERE tid IN (2,3)
-- 等同於
SELECT * FROM table1 WHERE tid=2 OR tid=3
-- 即使 tid 有索引也會失效,全表掃描

5. 能不用 NOT 就不用

改用正向邏輯(如 NOT IN → NOT EXISTS 或 LEFT JOIN ... WHERE IS NULL)。

6. EXISTS vs IN:效能其實一樣

許多資料稱 EXISTS 優於 IN,但實測 pubs 資料庫 統計 I/O 完全一致:

-- IN 寫法
SELECT title,price FROM titles WHERE title_id IN (SELECT title_id FROM sales WHERE qty>30)
-- EXISTS 寫法
SELECT title,price FROM titles WHERE EXISTS (SELECT * FROM sales WHERE sales.title_id=titles.title_id AND qty>30)
-- 兩者邏輯讀、掃描計數完全相同

結論:可讀性優先,無需為此刻意改寫。

7. CHARINDEX() 不比前置 % LIKE 快

實測 100 萬筆資料:

WHERE CHARINDEX('刑偵支隊', reader) > 0   -- 7 秒,邏輯讀 7155
WHERE reader LIKE '%刑偵支隊%'             -- 7 秒,邏輯讀 7155
-- 完全一樣,皆為全表掃描

8. UNION 不絕對優於 OR(關鍵在查詢欄位是否相同)

場景OR 寫法UNION 寫法結論
不同欄位
fariqi='...' OR gid>...
68 秒,全表掃描9 秒,索引掃描✅ UNION 勝出
相同欄位
fariqi='A' OR fariqi='B'
6.4 秒11.6 秒❌ OR 反而更快

原因:相同欄位 OR 可直接走索引範圍掃描;UNION 需合併結果集、去重,反增開銷。

9. SELECT * 是效能殺手

實測提取 1 萬筆:

  • SELECT gid, fariqi, reader, title → 4.7 秒
  • SELECT gid, fariqi, title → 1.4 秒
  • SELECT gid, fariqi → 0.08 秒

原則:需多少、選多少,減少網路傳輸與 I/O。

10. COUNT(*) 不比 COUNT(欄位) 慢,反而更快

寫法耗時說明
COUNT(*)1.5 秒自動選最小欄位統計
COUNT(主鍵)1.48 秒最直接
COUNT(非主鍵)3~52 秒欄位越長越慢

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

測試 TOP 10000 排序:

  • 無排序 / ORDER BY 聚集索引(fariqi) → ~170 毫秒,邏輯讀 290
  • ORDER BY 非聚集主鍵(gid) → ~4.7 秒,邏輯讀 4~5 萬

正序/倒序差異極小;大資料量(10 萬+)差距縮小但聚集仍優。

12. TOP 關鍵字:大資料分頁神器

超大資料集回應時間瓶頸在 物理 I/O。巢狀 TOP 可大幅減少回傳行數:

SELECT TOP 10 * FROM (
    SELECT TOP 10000 gid, fariqi, title FROM tgongwen
    WHERE neibuyonghu='辦公室' ORDER BY gid DESC
) AS a ORDER BY gid ASC
-- 子查詢雖讀 1 萬行,外層僅回傳 10 行,物理 I/O 極小

SQL Server 專有 TOP 經優化,Oracle 需用 ROWNUM 替代。

總結

  • 掌握 SARG 規則,寫出可優化條件
  • 避開 OR、NOT、函數、前置 % 等索引殺手
  • SELECT 列表精簡、善用 TOP 控制 I/O
  • 優化器聰明,但需開發者配合寫出「優化器友好」的 SQL
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved