述詞的評估順序
就在本系列的第 3 部分之前,我們短暫地停頓了一下,討論了 SQL 中的述詞,因為它們會導致與外部聯結相關的錯誤。在本系列「常見的 SQL 查詢錯誤」的最後一部分中,述詞將再次出現,因為我們將研究述詞的評估順序如何導致看似結構良好的查詢因錯誤而執行失敗。
述詞處理順序的概述
按照邏輯查詢處理順序,查詢按以下順序執行:
- FROM
- WHERE
- GROUP BY
- HAVING
- SELECT
因此,從邏輯上講,首先處理 FROM 子句以定義來源資料集。接下來,套用 WHERE 述詞以縮減結果集,然後是 GROUP BY,依此類推。
實際上,述詞的評估和處理順序遠沒有那麼嚴格,因為查詢最佳化工具可能會移動査詢中的運算式,以便產生最有效的資料擷取計劃。因此,在處理下一個子句之前,可能不會套用 WHERE 子句中的篩選。事實上,述詞在物理執行計劃中的套用可能比你預期的要遲得多。/p>
另一個讓資料庫開發人員感到困惑和沮喪的常見原因是,與大多數程式設計語言不同,述詞並不總是從左到右執行。這意味著,如果你有一個包含篩選「WHERE a=1 AND b=2」的 WHERE 子句,則不能保證會首先評估「a=1」。事實上,沒有簡單的方法可以僅透過查看查詢來判斷篩選將以什麼順序執行。
一個實際的例子
為了更好地理解述詞的評估順序,我們將對以下 accounts 資料表撰寫一個 SELECT 查詢,如 Navicat 16 的資料表設計器所示:
以下是我們將要查詢的一些範例資料:
在 account_number 欄中,業務帳戶被分配了一個數字識別碼,而個人帳戶被分配了一個由字元組成的識別碼。這不是很好的資料表設計,因為 account_number 欄應該由兩個不同的欄位表示,每個帳戶類型都應該有正確的資料類型,並且不應在同一個資料表中。儘管如此,我們通常無法變更資料表設計,所以必須按資料表的原樣處理。
因此,考慮到這一點,讓我們設計一個查詢來擷取所有 account_number 大於 50 的業務類型帳戶。查詢可能如下所示:
在某些資料庫中,查詢會產生錯誤:
Conversion failed when converting the varchar value 'ACFB' to data type int
只要查詢最佳化工具決定優先處理「CAST(account_number AS UNSIGNED INTEGER) > 50」述詞而不是「account_type LIKE 'Business%'」,查詢就會失敗。避免上述錯誤的最安全方法是:
- 正確設計資料表,避免在一欄中儲存混合資料。
或者
- 使用 CASE 運算式確保只有有效的數值才會轉換為 INTEGER 資料類型,如下所示:
總結
在這個關於「常見的 SQL 査詢錯誤」的系列文章中,我們探索了看似直觀的 SQL 查詢建構方法如何導致反模式,從而導致錯誤的結果和/或效能降低。尤其要注意述詞的位置和評估順序,因為它們會導致許多意想不到的問題。