實戰發現,惡劣 SQL 多源自:**索引設計不當**、**連接條件不足**、**WHERE 子句不可優化**。針對這三點精準優化,常獲數量級效能提升。本文以 Sybase ASE 11.0.3 (邏輯通用於 SQL Server/Oracle/PostgreSQL) 實測環境為例說明原則。
record:62 萬列,欄位 date、place、amount。| 索引策略 | 查詢 1 date 範圍 + amount > 2000 | 查詢 2 GROUP BY date SUM(amount) | 查詢 3 date 範圍 + place IN ('BJ','SH') |
|---|---|---|---|
| 1. date 非叢集索引 | 25 秒 | 55 秒 | 27 秒 |
| 2. date 叢集索引 | 14 秒 | 28 秒 | 14 秒 |
| 3. (place, date, amount) 組合索引 | 26 秒 | 27 秒 | < 1 秒 |
place,查詢 1/2 未引用 place → **索引無法使用**;查詢 3 引用 place 且所有欄位覆蓋於索引 → **索引覆蓋**,極速。date, OrderDate, LogTime)。CustomerID, Status, Type)。INCLUDE,避免 Key Lookup,實現「索引覆蓋」。sys.dm_db_index_usage_stats 找出 user_seeks+scans+lookups = 0 的索引刪除。
-- ❌ 缺少連接條件 (或條件不足)
SELECT * FROM Orders o, OrderDetails d, Products p
WHERE o.OrderDate > '2023-01-01';
-- 產生 3 表笛卡兒積,資料量爆炸
-- ✅ 完整連接條件
SELECT o.OrderID, d.Qty, p.ProductName
FROM Orders o
JOIN OrderDetails d ON o.OrderID = d.OrderID
JOIN Products p ON d.ProductID = p.ProductID
WHERE o.OrderDate > '2023-01-01';
| 模式 | 可優化 | 不可優化 (導致掃描) | 改寫方案 |
|---|---|---|---|
| 函數包裝欄位 | — | WHERE YEAR(OrderDate) = 2023 | WHERE OrderDate >= '2023-01-01' AND OrderDate < '2024-01-01' |
| 隱含轉換 | — | WHERE VarcharCol = 123 (數值比較字串) | WHERE VarcharCol = '123' 或統一型別 |
| 負向邏輯 | — | WHERE Status <> 'CANCELLED' | 改用 WHERE Status IN ('OPEN','SHIPPED',...) 或篩選索引 |
| OR 跨欄位 | — | WHERE Col1 = @Val OR Col2 = @Val | 拆分為 UNION ALL 兩查詢 (各用各索引) 或全文檢索 |
| LIKE 開頭萬用字元 | — | WHERE Name LIKE '%Smith' | 反轉儲存 + 正向 LIKE、全文檢索、n-gram 索引 |
| 參數嗅探 | — | 計畫針對首次參數最佳化,後續參數分布不同災難 | OPTION (RECOMPILE), OPTIMIZE FOR UNKNOWN, 區域變數賦值 |
-- 原始:30 秒
SELECT c.Name, SUM(o.Amount)
FROM Customers c, Orders o
WHERE c.CustID = o.CustID
AND YEAR(o.OrderDate) = 2023
AND c.Region IN ('North','East')
GROUP BY c.Name;
-- 優化後:0.3 秒
-- 1. 索引:Orders (OrderDate) 叢集索引 + INCLUDE (CustID, Amount)
-- 2. 連接:明確 JOIN + 完整 ON 條件
-- 3. WHERE:移除函數、使用範圍、Region 覆蓋索引
SELECT c.Name, SUM(o.Amount)
FROM Customers c
JOIN Orders o ON c.CustID = o.CustID
WHERE o.OrderDate >= '2023-01-01' AND o.OrderDate < '2024-01-01'
AND c.Region IN ('North','East')
GROUP BY c.Name;
-- 索引覆蓋:Customers (Region) INCLUDE (Name, CustID)
Table Scan, Key Lookup, Sort, Hash Match, Parallelism, Missing Index, Parameter Sniffing。ALTER INDEX ... REBUILD/REORGANIZE、UPDATE STATISTICS (全掃描/抽樣)。**索引設計決定上限,連接條件決定正確性,WHERE 子句決定能否命中索引。** 三者缺一不可。養成「先看計畫、再改 SQL、後驗證 IO/Time」閉環習慣,效能優化從玄學變為工程。