2009-04-17 63 views
20

在爲this question準備了答案之後,我發現我無法驗證我的答案。IN謂詞如何在SQL中工作?

在我的第一個編程工作中,我被告知IN()謂詞中的查詢會針對父查詢中包含的每一行執行,因此應避免使用IN

例如,假設查詢:

SELECT count(*) FROM Table1 WHERE Table1Id NOT IN (
SELECT Table1Id FROM Table2 WHERE id_user = 1) 
 
Table1 Rows | # of "IN" executions 
---------------------------------- 
     10 |  10 
    100 |  100 
    1000 |  1000 
    10000 | 10000 

這是正確的嗎? IN謂詞如何實際工作?

+4

非常好的問題。沒有看到許多問題要求對SQL中某些事情的實際工作進行細分。人們經常更關心獲得他們想要的結果。 – TheTXI 2009-04-17 16:35:08

回答

16

你有關於每一行執行子查詢的警告是真實的 - 爲相關子查詢。

SELECT COUNT(*) FROM Table1 a 
WHERE a.Table1id NOT IN (
    SELECT b.Table1Id FROM Table2 b WHERE b.id_user = a.id_user 
); 

注意,子查詢引用外部查詢的id_user列。 Table1的每一行上的值id_user可能不同。因此,子查詢的結果可能會有所不同,具體取決於外部查詢中的當前行。 RDBMS 必須多次執行子查詢,一次爲外部查詢中的每一行執行一次。

您測試的示例是非相關子查詢。大多數現代的RDBMS優化器值得他們的鹽應該能夠告訴子查詢的結果何時不取決於外部查詢的每一行中的值。在這種情況下,RDBMS會一次運行子查詢,緩存其結果,並在外部查詢中重複使用它作爲謂詞。

PS:在SQL中,IN()被稱爲「謂詞」,而不是語句。謂語是評估爲真或假的語言的一部分,但不一定能作爲語句獨立執行。也就是說,你不能只將它作爲SQL查詢來運行:「2 IN(1,2,3);」雖然這是一個有效的謂詞,但它不是一個有效的陳述。

0

不是。但它是用黃色來寫這樣的查詢使用JOIN

4

取決於優化器。檢查每個特定查詢的確切query plan以查看RDBMS如何實際執行該查詢。

在Oracle這會是:

EXPLAIN PLAN FOR «your query» 

在MySQL或PostgreSQL

EXPLAIN «your query» 
+1

@vartec - 閱讀查詢計劃的任何資源?這是我的知識的一個盲目的一面,我還沒有找到一個體面的文章在線。 – 2009-04-17 16:36:12

+0

這是數據庫管理系統的依賴,但基本上,這讓你知道它會做什麼基本的操作,並以什麼順序,它會使用哪個索引等。 – vartec 2009-04-17 16:39:32

+0

我認爲你必須尋找什麼,將是關於數據庫調整的文章,作爲這首先是查看查詢計劃的目的。 – vartec 2009-04-17 16:42:22

8

這將完全取決於你使用的數據庫,以及準確的查詢。

查詢優化器有時候非常聰明 - 在您的示例查詢中,我期望更好的數據庫能夠使用與連接相同的技術。更幼稚的數據庫可能會多次執行相同的查詢。

3

大多數SQL引擎如今幾乎總是產生相同的執行計劃LEFT JOIN,NOT IN和NOT EXISTS

我會說看你的執行計劃,如果你發現了:-)

而且對於Table1Id列有NULL值,您將不會收到任何數據

5

這取決於有問題的RDBMS

詳細信息,在這裏分析:

單位秒園藝:

  1. MySQL將優化查詢,以這樣的:

    SELECT COUNT(*) 
    FROM Table1 t1 
    WHERE NOT EXISTS 
         (
         SELECT 1 
         FROM Table2 t2 
         WHERE t2.id_user = 1 
           AND t2.Table1ID = t1.Table2ID 
         ) 
    

    和在一個循環中運行子查詢內,每次使用索引查找。

    • SQL Server將使用MERGE ANTI JOIN

    內部子查詢不會以通常的詞義「執行」,相反,查詢和子查詢的結果將同時獲取。

    有關詳細說明,請參閱上面的鏈接。

    • Oracle將使用HASH ANTI JOIN

    內部子查詢將被執行一次,並且將從結果集中構建一個哈希表。

    來自外部查詢的值將在哈希表中查找。

    • PostgreSQL將使用NOT (HASHED SUBPLAN)

    很像Oracle

注意,重寫查詢,因爲這:

SELECT (
     SELECT COUNT(*) 
     FROM Table1 
     ) - 
     (
     SELECT COUNT(*) 
     FROM Table2 t2 
     WHERE (t2.id_user, t2.Table1ID) IN 
       (
       SELECT 1, Table1ID 
       FROM Table1 
       ) 
     ) 

將大大提高這4個系統的性能。

0

是的,但只要查詢處理器「找到」您正在查找的值就停止執行......因此,例如,如果外部select中的第一行的Table1Id = 32,並且Table2有記錄用TABLEID = 32,然後 只要子查詢發現表2中的行,其中TABLEID = 32,它停止...