NOT IN 與 NOT EXISTS
在程式設計界,「反面模式(anti-pattern)」是一個常用術語。它指的是解決經常出現的問題的方法,而這方法不僅無效,而且有可能適得其反。這個術語最初是由電腦程式師 Andrew Koenig 於 1995 年在其著作《設計模式》中創造的,作為被認為既可靠又有效的設計模式的反義詞。
儘管 SQL 並不是一種真正的程式設計語言,但事實證明它同樣容易受到反面模式的影響,尤其是當間題中的查詢相當複雜時。有時錯誤很難被發現,直到查詢在生產環境中使用時,錯誤才會暴露出來。
為了儘早發現 SQL 錯誤,接下來的幾篇文章將重點介紹一些最常見的錯誤。我們將使用 MySQL 來作為今天的範例,但這些概念在任何類型的 SQL 中都同樣有效。
NOT IN 與 NOT EXISTS
一種常見類型的 SELECT 查詢可以擷取未包含在值清單中的資料。舉例來說,這裡有兩個在 Navicat for MySQL 16 中建立的非常簡單的資料表。第一個資料表儲存色彩資料:
第二個資料表儲存產品資料:
我們想要做的是選取所有尚未與任何產品關聯的色彩。換句話說,我們需要建置一個查詢,只傳回那些產品沒有的色彩。我們可以嘗試使用 NOT IN 述詞來取得相關記錄。
我們預計以下查詢傳回兩列(「black」和「green」),而實際上它傳回的是一個空白結果集:
問題出在哪裡?products 資料表的 color 欄中存在 NULL 值,由 NOT IN 述詞轉換為:
color NOT IN (Red, Blue, NULL)
或
NOT(color=Red OR color=Blue OR color=NULL)
運算式「color=NULL」的計算結果為 UNKNOWN,許多資料庫開發人員忽略的是,NOT UNKNOWN 也計算結果為 UNKNOWN!結果,所有列都被過濾掉,查詢會傳回零列。
如果需求發生變化,這個問題也會浮出水面,並且會更新一個不可為 NULL 的欄為允許 NULL。因此,即使欄在初始設計中不允許使用 NULL,以防萬一你也應該確保查詢在使用 NULL 時繼續正常工作。
最簡單的解決方案是使用 EXISTS 述詞而不是 IN:
問題解決了!
那麼,為什麼會這樣呢?IN 關鍵字將比較相應子查詢欄中的所有值,而 EXISTS 則評估真或假。因此,使用 IN 運算子時,SQL 引擎將掃描從內部查詢取得的所有記錄。另一方面,如果我們使用的是 EXISTS,SQL 引擎將在找到符合項目後立即停止掃描程序。
結論
在有關常見的 SQL 查詢錯誤的第一部分中,我們了解了反面模式如何出現在 SELECT 查詢中,首先是錯誤地使用 NOT IN 述詞。
如果你想試用 Navicat 16 for MySQL,你可以在這裡下載 14 天的試用版。