許多開發者擔心 WHERE name='zhangsan' AND tID > 10000 與條件順序互換會影響效能。實則 SQL Server 查詢優化器 會自動分析 WHERE 子句,選擇最佳索引縮小掃描範圍,條件順序不影響執行計畫。但理解優化器運作原理,才能寫出真正可優化的 SQL。
優化器在查詢分析階段判斷條件是否可作為「掃描參數」(SARG)。符合 SARG 形式才能利用索引快速定位:
欄位名 操作符 <常數 或 變數>
或
<常數 或 變數> 操作符 欄位名
符合 SARG 範例:
Name = '張三'價格 > 50005000 < 價格Name = '張三' AND 價格 > 5000不符合 SARG 的表達式 → 索引失效 → 全表掃描。
name LIKE '張%' → ✅ SARG,索引生效name LIKE '%張' → ❌ 非 SARG,全表掃描(前置 % 導致索引無法定位)Name='張三' AND 價格>5000 → ✅ SARGName='張三' OR 價格>5000 → ❌ 非 SARG,全表掃描改寫建議:用 UNION ALL 分拆(見第 8 點)。
典型非 SARG:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE 及欄位上使用函數:
ABS(價格) < 5000 ❌Name LIKE '%三' ❌特例:價格*2 > 5000 優化器可轉為 價格 > 2500,但不建議依賴隱式轉換,可能不等價。
SELECT * FROM table1 WHERE tid IN (2,3)
-- 等同於
SELECT * FROM table1 WHERE tid=2 OR tid=3
-- 即使 tid 有索引也會失效,全表掃描
改用正向邏輯(如 NOT IN → NOT EXISTS 或 LEFT JOIN ... WHERE IS NULL)。
許多資料稱 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)
-- 兩者邏輯讀、掃描計數完全相同
結論:可讀性優先,無需為此刻意改寫。
實測 100 萬筆資料:
WHERE CHARINDEX('刑偵支隊', reader) > 0 -- 7 秒,邏輯讀 7155
WHERE reader LIKE '%刑偵支隊%' -- 7 秒,邏輯讀 7155
-- 完全一樣,皆為全表掃描
| 場景 | OR 寫法 | UNION 寫法 | 結論 |
|---|---|---|---|
不同欄位fariqi='...' OR gid>... | 68 秒,全表掃描 | 9 秒,索引掃描 | ✅ UNION 勝出 |
相同欄位fariqi='A' OR fariqi='B' | 6.4 秒 | 11.6 秒 | ❌ OR 反而更快 |
原因:相同欄位 OR 可直接走索引範圍掃描;UNION 需合併結果集、去重,反增開銷。
實測提取 1 萬筆:
SELECT gid, fariqi, reader, title → 4.7 秒SELECT gid, fariqi, title → 1.4 秒SELECT gid, fariqi → 0.08 秒原則:需多少、選多少,減少網路傳輸與 I/O。
| 寫法 | 耗時 | 說明 |
|---|---|---|
COUNT(*) | 1.5 秒 | 自動選最小欄位統計 |
COUNT(主鍵) | 1.48 秒 | 最直接 |
COUNT(非主鍵) | 3~52 秒 | 欄位越長越慢 |
測試 TOP 10000 排序:
正序/倒序差異極小;大資料量(10 萬+)差距縮小但聚集仍優。
超大資料集回應時間瓶頸在 物理 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 替代。