在編寫SQL時,會建議將選擇性高(過濾數據多)的條件放到WHERE條件的前面,這是為了讓查詢優化器優先考慮這些條件,減少生成最優(或相對最優)的執行計划的時間,但最終的執行計划生成過濾順序還是決定這些條件的選擇性與判斷bool值的容易程度
測試代碼:
GO SELECT * INTO #T1 FROM sys.all_columns GO SELECT * INTO #T2 FROM sys.all_columns GO SELECT * INTO #T3 FROM sys.all_columns GO SET STATISTICS IO ON SET STATISTICS TIME ON GO SELECT * FROM #T1 AS T1 WHERE T1.[object_id]=3 AND (SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1 SELECT * FROM #T1 AS T1 WHERE (SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1 AND T1.[object_id]=3
執行計划:
可以從查詢計划看出,無論T1.[object_id]=3在何處,其計算bool值相對簡單,而(SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1 需要訪問其他表,因此執行優化器優先執行T1.[object_id]=3,在滿足T1.[object_id]=3為ture時再堅持行是否滿足(SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1。
但對於以下查詢:
SELECT * FROM #T1 AS T1 WHERE (SELECT COUNT(1) FROM #T3 AS T3 WHERE T3.[object_id]>T1.[object_id])<1 AND (SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1 SELECT * FROM #T1 AS T1 WHERE (SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1 AND (SELECT COUNT(1) FROM #T3 AS T3 WHERE T3.[object_id]>T1.[object_id])<1
執行計划:
執行統計:

(25 row(s) affected) Table 'Worktable'. Scan count 29, logical reads 36813, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table '#T3_________________________________________________________________________________________________________________00000000000C'. Scan count 4, logical reads 80, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table '#T1_________________________________________________________________________________________________________________000000000009'. Scan count 5, logical reads 59, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table '#T2_________________________________________________________________________________________________________________00000000000A'. Scan count 1, logical reads 59, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. (1 row(s) affected) SQL Server Execution Times: CPU time = 111 ms, elapsed time = 331 ms. (25 row(s) affected) Table '#T1_________________________________________________________________________________________________________________000000000009'. Scan count 5, logical reads 59, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'Worktable'. Scan count 10731, logical reads 87653, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table '#T3_________________________________________________________________________________________________________________00000000000C'. Scan count 4, logical reads 236, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table '#T2_________________________________________________________________________________________________________________00000000000A'. Scan count 1, logical reads 59, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. (1 row(s) affected) SQL Server Execution Times: CPU time = 219 ms, elapsed time = 281 ms.
條件 (SELECT COUNT(1) FROM #T2 AS T3 WHERE T3.[object_id]>T1.[object_id])<1 能過濾掉大部分數據,而(SELECT COUNT(1) FROM #T3 AS T2 WHERE T2.column_id>T1.column_id)>1不能過濾任何數據,因此如果優先執行(SELECT COUNT(1) FROM #T3 AS T2 WHERE T2.column_id>T1.column_id)>1,則會大大減少判斷(SELECT COUNT(1) FROM #T2 AS T2 WHERE T2.column_id>T1.column_id)>1的次數,從而提高查詢速度,但SQL Server無法推斷出該結論,因此只能順序判斷WHERE 條件。
總結:雖然在很多情況下SQL Sever引擎能幫助我們判定 WHERE 條件后的執行順序,但我們仍應該將選擇性高(過濾數據多)的條件放置在 WHERE 語句中的前面,尤其對於復雜的SQL 語句,應仔細分析測試。
你比SQL SERVER 更了解你的數據!!!