查詢操作佔數據庫操作最大比重,SELECT 是成本最高的 SQL 語句。百萬級表全掃描耗時數十分鐘,優化後可降至分鐘級。本文結合 Informix/Oracle/SQL Server 實戰,系統闡述查詢優化核心技術。
「查詢優化是 DBMS 的事,與我寫的 SQL 無關」——**錯誤**。優化器基於用戶 SQL 生成計劃,糟糕的 SQL 經優化器仍難變高效。用戶 SQL 質量是優化基石。
主流產品(Oracle、Sybase、SQL Server、Informix)均採用**基於代價的優化 (CBO)**,根據系統字典統計信息估算代價選擇最優計劃。
| 原則 | 說明 |
|---|---|
| 頻繁 JOIN 非 FK 列建索引 | FK 列通常已有索引,非 FK 高頻 JOIN 列需手動建立 |
| 頻繁排序/分組列建索引 | GROUP BY、ORDER BY 列 |
| 高選擇性條件列建索引 | 不同值多的列(如身份證號),低選擇性列(如性別)別建索引 |
| 多列排序建複合索引 | 列順序匹配 ORDER BY 順序 |
| 定期檢查/重建索引 | Informix tbcheck、SQL Server DBCC SHOWCONTIG/ALTER INDEX REBUILD,大量更新後重建索引提速顯著 |
優化器避免排序的條件:
GROUP BY/ORDER BY 列順序與索引順序一致不可避免時簡化: 縮小排序列範圍、合併表(犧牲正規化換效率)。
嵌套查詢中順序存取災難性:3 層嵌套 × 1000 行 = 10 億行掃描。
核心對策: **連接列必須建索引**。
並集優化: 某 WHERE 條件強制順序掃描時,改用 UNION ALL 拆分:
-- 強制順序掃描的原語句
SELECT * FROM orders
WHERE (customer_num = 104 AND order_num > 1001) OR order_num = 1008
-- 優化:拆分為兩個索引可用的查詢再合併
SELECT * FROM orders WHERE customer_num = 104 AND order_num > 1001
UNION ALL
SELECT * FROM orders WHERE order_num = 1008
IN / EXISTS 選擇:外表大、內表小 → EXISTS;外表小、內表大且有索引 → INJOIN 或 APPLY (SQL Server)NOT IN 含 NULL 陷阱 → 改用 NOT EXISTSUPDATE STATISTICS / sp_updatestats 定期執行AUTO_UPDATE_STATISTICS(默認開啟),但大表需手動配合全掃描更新SET SHOWPLAN_ALL ON / SSMS 圖形化計劃STATISTICS IO / STATISTICS TIME 量化 I/O 與 CPUDBCC SHOWCONTIG / sys.dm_db_index_physical_stats 檢查碎片查詢優化無銀彈,核心是:**寫出友好於優化器的 SQL(索引友好、避免排序、消除順序掃描)+ 維護準確統計信息**。以執行計劃為鏡,以業務負載為標,持續迭代。