2012-08-03 9 views
4

我正在查詢的兩個表都有大約1.5億行。令人困惑的性能增益與嵌套選擇*不存在

下面的語句我結束之後,在沒有45分鐘回來,所以我不知道多久會運行:

select * from Cats outside 
    where not exists(select * from Cats cat 
        where exists(select dog.foo,dog.bar from Dogs dog 
         where cat.foo = dog.foo 
         and cat.bar = dog.bar))); 

select * from Cats cat 
where not exists(select dog.foo,dog.bar from Dogs dog 
        where cat.foo = dog.foo 
        and cat.bar = dog.bar); 

但此查詢在約3分鐘執行

我的問題是幕後發生了什麼,我看到了這種性能增益?

背後返回相同的結果集

推理:

第一個查詢(慢)規定給予不存在的基礎上,貓表中的所有元素。

第二個查詢(快速)聲明給存在的貓的子集中不存在的所有元素。

我期望以下查詢:

select dog.foo,dog.bar from Dogs dog 
          where cat.foo = dog.foo 
          and cat.bar = dog.bar 

返回[A,B,C]

這是常見的兩種功能。

我的貓表有以下幾點:[A,B,C,d,E]

我希望下面的查詢:

select * from Cats cat 
        where exists 

返回[A,B,C] 和最後一塊:

select * from Cats outside 
     where not exists 

返回[d,E]

UPD ATE

集符號在數學上證明我的要求(請糾正我,如果我使用了錯誤的符號):

∀ Cat (Ǝ cat ≠ Ǝdog)  

對於貓的所有元素,返回一個包含沒有貓的每個元素的集合在狗

∀ Cat (Ǝ cat = Ǝdog) 

對於貓的所有元素等於一個元素,返回一個包含不相等的元素在狗貓的每個元素設定

∀ Cat (Ǝ innerCat ≠ Ǝcat) 

對於貓的所有元素,返回一個包含不貓

二更新

我看到我的數學不與排隊等於元素內貓的每個元素的集合我SQL。

+1

您是否嘗試過EXPLAIN來查看計劃? – podiluska 2012-08-03 14:09:54

+0

@podiluska當我做一個解釋計劃它不會產生任何東西。當我運行select語句來驗證計劃是。 – Woot4Moo 2012-08-03 14:12:09

+1

他們確實返回不同的結果集,這可能會解釋性能增益.... – podiluska 2012-08-03 14:15:28

回答

0

我已經通過測試,這是最初的問題進行查詢的最有效的方式發現:

Select cat.foo,cat.bar from cats cat 

MINUS 

Select dog.foo,dog.bar from dogs dog 

這工作,因爲沒有我的列是空。

-1

他們是不同的查詢結果不同。要使第二次返回與第一次相同,它需要類似於...

select * from cats outside 
    where not exists(select * from Cats cat 
        where exists(select dog.foo,dog.bar from Dogs dog 
         where cat.foo = dog.foo  
         and cat.bar = dog.bar) 
         and outside.foo = cat.foo 
         and outside.bar=cat.bar 
        ) 
+0

看到我更新的問題來解釋我的邏輯 – Woot4Moo 2012-08-03 14:26:43

+0

第二個查詢檢查任何匹配狗的表 - 所以要麼返回所有的貓,要麼沒有。首先檢查那些存在。順便說一句 - @MicSim的左連接應該給你你想要的 – podiluska 2012-08-03 14:33:38

1

您的問題的答案是檢查執行計劃。

一點題外話,你應該試試這個等效的查詢(見https://stackoverflow.com/a/1069467/44522):

SELECT * FROM Cats cat LEFT OUTER JOIN Dogs dog 
    ON cat.foo = dog.foo and cat.bar = dog.bar 
WHERE dog.foo IS NULL and dog.bar IS NULL 

我打賭它會執行方式更快(假設你有正確的索引)。

+0

你需要(和dog.bar爲空)? – podiluska 2012-08-03 14:28:56

+0

是的,如果你沒有明確地排除它,你可能會有dog.foo = NULL和dog.bar ='xyz',如果你沒有明確地排除它,那麼這是一個非常不可能的情況。外鍵 – MicSim 2012-08-03 14:32:00

3

顯然,不在和不存在是數據引擎優化的問題。從技術上講,這些被稱爲反連接(區別於等連接,半連接,非平等連接等)。

當一個連接很難優化時,引擎會使用嵌套循環連接。這些通常是性能最差的類型(儘管在SQL Server執行計劃中,它們通常看起來相同,因爲SQL Server在執行計劃中將索引查找稱爲「嵌套循環」)。

這兩個查詢有什麼區別?第一個只有一個NOT EXISTS,所以它可能做一些效率低下的事情。第二個是在最內層的子查詢上做一個EXISTS。這首先被優化,基本上作爲一個連接。如果鍵索引都很好。 SQL Server也可以爲這些選擇基於散列或基於合併的算法。

第二版中的「不存在」基於同一張表。這可能會給SQL Server更多空間進行優化。

最後,第二個版本可能會大大減少數據集。如果是這樣,即使外部的嵌套循環連接可能會更快。

2

執行時,第二個查詢是更優化,這就是爲什麼:

您別名外查詢的Catsoutside,但你不要指outsidewhere not exists的子句。因此,SQL可以做到以下幾點:

  • 找到任何一個貓在那裏(從最內層查詢)
  • 這意味着有確實存在,以滿足您的外where not exists所有貓outside
  • 因此where not exists子句false所有行中outside
  • 因此,對於查詢結果爲空

您的第一個查詢必須重新執行表中每個cat的嵌套查詢,因此速度較慢。

+0

我看到我的問題是,我的數學(上面說過)與我的查詢並不完全匹配,這幫助我看到我做錯了什麼。 – Woot4Moo 2012-08-03 15:31:01

+0

有沒有辦法避免r電子執行內部查詢?如果我一次只能做一次這樣的查詢,那將是有益的。 – Woot4Moo 2012-08-03 16:27:26