-- 差:欄位參與運算,索引失效
WHERE -col1 = 5
WHERE col1 + 10 > 20
WHERE UPPER(name) = 'JOHN'
WHERE YEAR(create_date) = 2023
-- 好:運算移至常數側,索引可用
WHERE col1 = -5
WHERE col1 > 10
WHERE name = 'JOHN' -- 配合大小寫不敏感排序規則
WHERE create_date >= '2023-01-01' AND create_date < '2024-01-01'
原理:SARGable (Search ARGument Able) — 欄位需「裸露」在比較運算子一側,優化器才能利用索引 Seek。
-- 差:隱含轉換,索引失效
WHERE int_col = '123'
WHERE varchar_col = 123
-- 好:顯式型別一致
WHERE int_col = 123
WHERE varchar_col = '123'
原理:字串與數值比較會雙向轉換為浮點數,導致索引不可用。
-- 差:DATEDIFF 導致索引失效
WHERE DATEDIFF(day, create_date, GETDATE()) = 0
-- 好:範圍查詢,索引可用
WHERE create_date >= CAST(GETDATE() AS DATE)
AND create_date < DATEADD(day, 1, CAST(GETDATE() AS DATE))
同理:ISNULL(col, '') = '' → col IS NULL OR col = ''(或建立過濾索引)
-- 差:OR 通常導致掃描
WHERE col1 = 1 OR col2 = 2
-- 好:UNION ALL 分別走索引再合併
SELECT * FROM T WHERE col1 = 1
UNION ALL
SELECT * FROM T WHERE col2 = 2
-- 好:IN 列表(單欄位多值)
WHERE status IN (1,2,3)
-- 好:EXISTS 替代 IN 子查詢
WHERE EXISTS (SELECT 1 FROM Ref WHERE Ref.ID = T.RefID AND Ref.Type = 'A')
例外:同一欄位多值 col IN (1,2,3) 會優化為 Seek;不同欄位 OR 才會掃描。
| 模式 | 優化方向 |
|---|---|
| LIKE '%keyword%' | 前導萬用字元無法用索引 → 全文檢索 / 反轉索引 / n-gram |
| != / <> / NOT IN | 通常掃描 → 改寫為 NOT EXISTS / LEFT JOIN ... WHERE IS NULL |
| 隱含轉換 | 顯式 CAST/CONVERT 或修正參數型別 |
| SELECT * | 明確列出欄位,減少 I/O、鎖爭用、網路傳輸 |
| 子查詢 | 改為 JOIN / APPLY / CTE,避免重複執行 |
| UNION | 無重複需求改用 UNION ALL,省去排序去重 |
SET STATISTICS IO, TIME ON — 邏輯讀取、CPU、耗時sys.dm_exec_query_stats — 歷史執行統計