2012-10-17 70 views
6

我有一個表foo與(其他20個)列bar,bazquux與索引bazquux。該表有500k行。爲什麼MAX()比ORDER BY ... LIMIT 1慢100倍?

爲什麼以下查詢在速度上差異如此之大?查詢A需要0.3s,而查詢B需要28s。

查詢

select baz from foo 
    where bar = :bar 
    and quux = (select quux from foo where bar = :bar order by quux desc limit 1) 

解釋

id select_type table type possible_keys key  key_len ref  rows Extra 
1 PRIMARY  foo  ref  quuxIdx   quuxIdx 9  const 2  "Using where" 
2 SUBQUERY foo  index NULL   quuxIdx 9  NULL 1  "Using where" 

查詢乙

select baz from foo 
    where bar = :bar 
    and quux = (select MAX(quux) from foo where bar = :bar) 

說明

id select_type table type possible_keys key  key_len ref  rows Extra 
1 PRIMARY  foo  ref  quuxIdx   quuxIdx 9  const 2  "Using where" 
2 SUBQUERY foo  ALL  NULL   NULL NULL NULL 448060 "Using where" 

我使用MySQL 5.1.34。

+0

'LiMIT 1'意味着採取1行並停止,不是嗎?查詢B是O(n * m) – jondinham

+2

@PaulDinh似乎兩個查詢的結果都是相同的,最有可能與操作順序有關,第一種情況下它通過quux和搜索欄從結果中快速排序(快速)搜索欄(需要檢查整個表)從未分類,然後排序,以找到最大 –

+0

@Viktor,你可以請顯示 '解釋選擇baz從foo 其中bar =:bar 和quux =(選擇quux從foo其中quux = MAX(quux)and bar =:bar)' '說明從foo中選擇baz 其中bar =:bar 和quux =(從foo選擇quux,其中quux = MAX(quux)和bar =:bar limit 1)' –

回答

6

您應該在(bar, quux)上添加索引。

沒有這個索引,MySQL無法看到如何有效地執行查詢,所以它必須從各種低效的查詢計劃中進行選擇。

在第一個示例中,它掃描quux索引,並找到找到的每一行,在原始表中查找bar的對應值。這需要兩倍的時間來檢查每一行,但幸運的是,具有正確值bar的行在其掃描開始附近,因此可以停止。這可能是因爲您搜索的bar的值經常出現,所以幸運的機率非常高。因此,在查找匹配之前,可能只需檢查少量行,即使檢查每行需要兩倍的時間,但只檢查了幾行的事實可以大大節省整體資源。由於您沒有bar上的索引,因此MySQL並不會提前知道值:bar會頻繁發生,因此無法知道此查詢會很快。

在第二個示例中,它使用一個不同的計劃,它總是掃描整個表。每行都直接從表中讀取,而不使用索引。這意味着每行讀取速度都很快,但由於行數很多,所以整體速度很慢。如果在:bar上沒有匹配的行,這將是更快的查詢計劃。但是,如果大約1%的行具有期望值bar,則與上述計劃相比,使用此查詢計劃的速度將會(非常)大約慢100倍。由於您在bar上沒有索引,因此MySQL並不提前知道這一點。

您也可以只添加缺少的索引,然後兩個查詢將更快

+1

似乎OP問爲什麼相同的結果有如此劇烈的差異 –

+0

所以'從foo中選擇quux,其中quux = MAX(quux)和bar =:bar'快於'從foo選擇MAX(quux)where bar =:bar'如果quux被索引int並且bar是文本? –

+0

nvm它給出了不同的結果:) –

相關問題