搗亂的子査詢
在這個關於「常見的 SQL 查詢錯誤」的系列中,我們已經看了幾個 SQL 查詢的範示,這些查詢在第一次檢查時看起來非常可靠,但它們有機會導致錯誤的結果和/或效能降低。上週,我們學習了放置謂詞的位置如何對查詢執行產生負面影響,尤其是在外部聯結中。今天將重點介紹子查詢,以及當任何基礎資料表變更時它們如何破壞 SQL 陳述式。
單一值與多個值子査詢
在比較單一值和多個值子查詢之前,我們應該簡單介紹一下子查詢是什麼。子查詢是指巢狀於較大的查詢中的完整 SQL 查詢。子查詢可以放在 SELECT、FROM 和 WHERE 子句中。
既然我們知道了子查詢是什麼,以及它在查詢中的位置,應該注意的是,與任何 SELECT 查詢一樣,子查詢傳回可能是一列,亦可能是多列。這種區別非常重要,因為它會影響你撰寫查詢語句的方式。例如,以下是在 Navicat Premium 16 中針對 Sakila 範例資料庫的查詢,它擷取了電影「ALONE TRIP」的所有演員:
由於應該只有一部名為「ALONE TRIP」的電影,我們可以使用等號(=)運算子來比對 film_id。
將上述查詢與以下查詢進行對比:
在這種情況下,子查詢選取電影的所有演員。當然,這個子查詢將傳回多列。此種情況下,我們應該使用 IN() 函式來比對 actor_id。
單列子查詢如何破壞
如前所述,可以在 SELECT 子句中放置一個子查詢,以擷取與主查詢資料表相關的欄。例如,以下在 Navicat Data Modeler 中兩個相關的 products 和 factories 資料表:
products 和 factories 資料表使用共同的 sku 欄位連結。
現在,讓我們撰寫一個查詢來提取每個產品的 factory_id。方法是使用相關子查詢撰寫查詢以擷取產品 factory_id:
請注意,這裡的重點是說明一種更有效的方法來擷取相同的資料。無論如何,我們得到了正確的結果集,一切都很好。
該查詢將繼續運行良好,直至公司決定隨著銷售額增加而建立新工廠的那一天到來:
factories 資料表中額外的列導致我們的查詢現在發生錯誤:
該錯誤告訴我們外部查詢需要一個純量值,但我們的子查詢傳回了一個結果集。我們可以使用 JOIN 解決這個問題並列出生產每種產品的所有工廠:
還有一件事...
請注意,在針對子查詢測試欄或運算式的任何子句中都可能出現相同的錯誤,例如「column = (SELECT value FROM Table)」。在這種情況下,解決方案是使用 IN() 函式而不是等號比較(=)運算子。