翻譯|使用教程|編輯:楊鵬連|2021-01-06 11:30:22.540|閱讀 325 次
概述:Phil Factor解釋了在使用子查詢比較數據集時,為什么您更應該使用[NOT] EXISTS而不是[NOT] IN。盡管不再具有顯著的性能優勢,但是當子查詢的源數據包含NULL值時,使用NOT EXISTS可以避免出現意外結果。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
SQL Prompt是一款實用的SQL語法提示工具。SQL Prompt根據數據庫的對象名稱、語法和代碼片段自動進行檢索,為用戶提供合適的代碼選擇。自動腳本設置使代碼簡單易讀--當開發者不大熟悉腳本時尤其有用。SQL Prompt安裝即可使用,能大幅提高編碼效率。此外,用戶還可根據需要進行自定義,使之以預想的方式工作。
在使用子查詢比較數據集時,過去曾經是EXISTS邏輯運算符比IN更快。例如,在查詢必須執行特定任務的情況下,但僅當子查詢返回任何行時,然后在評估WHERE [NOT] EXISTS(子查詢)時,數據庫引擎只要發現一個就可以退出搜索行,而WHERE [NOT] IN(子查詢) 將始終在進一步處理之前從子查詢中收集所有結果。
但是,查詢優化器現在會盡可能以相同的方式對待EXISTS和IN,因此您不太可能看到任何明顯的性能差異。但是,如果子查詢的源數據包含NULL值,則在使用NOT IN運算符時需要謹慎。如果是這樣,則應考慮使用NOT EXISTS運算符而不是NOT IN,或者將語句重鑄為左外部聯接。
SQL Prompt(PE019)中的代碼分析規則中包含了建議使用[NOT] EXISTS而不是[NOT] IN的建議。
哪種效果更好:EXISTS或IN ....?
有兩種方法可以計算出兩個數據集之間的差異,但是最常見的兩種方法是使用EXISTS或IN邏輯運算符。想象一下,我們有兩個簡單的表,一個表包含英語中的所有常用單詞(CommonWords),另一個表包含Bram Stoker的“ Dracula”中的所有單詞的列表(WordsInDracula)。該TestExistsAndIn下載包括腳本來創建這兩個表,并填充和與之相關的文本文件中每一個。通常,在沙盒服務器中擁有這樣的表對于在進行開發工作時運行測試很有用,盡管您可以選擇使用的書!
在德古拉語中有多少個不常見的單詞?假設NULL該CommonWords.Word列中沒有值(稍后會詳細介紹),則以下查詢將返回相同的結果(1555個字),并具有相同的執行計劃,這在兩個之間使用了合并聯接(Right Anti Semi Join)表。
--using NOT IN SELECT Count(*) FROM dbo.WordsInDracula WHERE word NOT IN (SELECT CommonWords.word FROM dbo.CommonWords); --Using NOT EXISTS SELECT Count(*) FROM dbo.WordsInDracula WHERE NOT EXISTS (SELECT * FROM dbo.CommonWords WHERE CommonWords.word = WordsInDracula.word);清單1
簡而言之,SQL Server優化器以相同的方式處理任一查詢,它們也將執行相同的查詢。
…或任何其他(除內部聯接,外部聯接或相交之外)?
什么其他所有可能的技術,但是,如使用ANY,EXCEPT,INNER JOIN,OUTER JOIN或INTERSECT?清單2顯示了我可以輕松想到的另外七個替代方案,盡管還有其他替代方案。
--using ANY SELECT Count(*) FROM dbo.WordsInDracula WHERE NOT(WordsInDracula.word = ANY (SELECT word FROM commonwords )) ; --Right anti semi merge join --using EXCEPT SELECT Count(*) FROM ( SELECT word FROM dbo.WordsInDracula EXCEPT SELECT word FROM dbo.CommonWords ) AS JustTheUncommonOnes; --Right anti semi merge join --using LEFT OUTER JOIN SELECT Count(*) FROM dbo.WordsInDracula LEFT OUTER JOIN dbo.CommonWords ON CommonWords.word = WordsinDracula.word WHERE CommonWords.word IS NULL; --right outer merge join --using FULL OUTER JOIN SELECT Count(*) FROM dbo.WordsInDracula full OUTER JOIN dbo.CommonWords ON CommonWords.word = WordsinDracula.word WHERE CommonWords.word IS NULL; --Full outer join implemented as a merge join. --using intersect to get the difference SELECT (SELECT Count(*) FROM WordsInDracula)-Count(*) FROM ( SELECT word FROM dbo.WordsInDracula intersect SELECT word FROM dbo.CommonWords ) AS JustTheUncommonOnes; --inner merge join --using FULL OUTER JOIN syntax to get the difference SELECT Count(*)-(SELECT Count(*) FROM CommonWords) FROM dbo.WordsInDracula full OUTER JOIN dbo.CommonWords ON CommonWords.word = WordsinDracula.word --full outer merge join --using INNER JOIN syntax to get the difference SELECT (SELECT Count(*) FROM WordsinDracula)-Count(*) FROM dbo.WordsInDracula INNER JOIN dbo.CommonWords ON CommonWords.word = WordsinDracula.word --inner merge join清單2
所有這9個查詢都給出相同的結果,但有沒有一種方法的效果更好?讓我們將它們全部放入一個簡單的測試工具中,看看每個版本需要多長時間!再次,代碼下載文件包括測試工具代碼以及所有九個查詢。
結果表明,盡管查詢看起來有很大不同,但對于優化程序而言,它通常只是“語法糖”。無論您的SQL有多優雅,優化器都只會聳聳肩,并提出執行它的有效計劃。實際上,前四個都使用完全相同的“正確的半合并合并”執行計劃,并且都花費相同的時間。
NOT IN的陷阱
比較具有空值的集合存在一定的不現實性,但是如果在每天的數據庫報告熱中發生這種情況,則可能會出錯。如果NULL子查詢或表達式的結果中有一個值傳遞給IN邏輯運算符,則它將給出合理的響應,并且與等效值相同EXISTS。但是,NOT IN行為卻大不相同。
清單3演示了這個問題。我們在@someWord表變量中插入三個常用詞和三個不常用詞,并且我們想知道不在表變量中的常用詞的數量。
SET NOCOUNT ON; DECLARE @someWord TABLE ( word NVARCHAR(35) NULL ); INSERT INTO @someWord ( word ) --three common words SELECT TOP 3 word FROM dbo.commonwords ORDER BY word DESC; -- three uncommon words INSERT INTO @someWord ( word ) VALUES ('flibberty'), ('jibberty'), ('flob'); SELECT [NOT EXISTS without NULL] = COUNT(*) FROM commonwords AS MyWords WHERE NOT EXISTS ( SELECT word FROM @someWord AS s WHERE s.word LIKE MyWords.word ); SELECT [NOT IN without NULL] = COUNT(*) FROM commonwords AS MyWords WHERE word NOT IN ( SELECT word FROM @someWord ); --Insert a NULL value INSERT INTO @someWord ( word ) VALUES (NULL); SELECT [NOT EXISTS with NULL] = COUNT(*) FROM commonwords AS MyWords WHERE NOT EXISTS ( SELECT word FROM @someWord AS s WHERE s.word LIKE MyWords.word ); SELECT [NOT IN with NULL] = COUNT(*) FROM commonwords AS MyWords WHERE word NOT IN ( SELECT word FROM @someWord );清單3
在NOT IN查詢時,才插入NULL到@someword,并且兩個NOT EXISTS查詢,所有正確地告訴我們,60385點的話是不是在我們的表變量,因為三都,并有在所有60388個常用詞。但是,如果子查詢可以返回NULL,則NOT IN根本不返回任何行。
從邏輯上講,SQL Server評估子查詢,將其替換為其返回的值列表,然后評估[NOT] IN條件。對于IN我們查詢的變體,這不會引起問題,因為它可以解決以下問題:
WHERE word = 'flibberty' OR word = 'jibberty' OR word = 'flob' OR word = 'zygotes' OR word = 'zygote' OR word = 'zydeco' OR word = NULL;對于“ z…”字樣的匹配項,將返回3行。附帶了刺NOT IN,它可以解決以下問題:
WHERE word <> 'flibberty' AND word <> 'jibberty'AND word <> 'flob' AND word <> 'zygotes' AND word <> 'zygote' AND word <> 'zydeco' AND word <> NULL;AND具有要比較的條件的結果NULL為'unknown',因此表達式將始終返回零行。這不是錯誤;這是設計使然。您可以辯稱,NULL不應在要使用NOT IN表達式的任何列中使用a ,但是在我們的實際工作中,這些東西可能會滲入表源。值得謹慎。因此,請使用EXISTS變體或其他變體,或始終記住WHERE在IN條件中包含一個從句以消除NULLs。 試用下載>>>
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自: