2013-08-25 232 views
0

我試圖優化以下查詢:爲VAR1 IN(1,2,...)GROUP BY(VAR2)ORDER BY SUM(VAR3)LIMIT適當的指數X

  SELECT name 
      FROM tbl 
      WHERE user_id 
       IN (".$user_ids.") 
      GROUP BY name ORDER BY SUM(counter) DESC LIMIT 10 

TBL信息: name是VARCHAR,counter和user_id是INT。 user_id,名稱是唯一的。

我試過添加IDX(user_id, counter, name),但在EXPLAIN我仍然看到Using where; Using index; Using temporary; Using filesort,所以我想我做錯了什麼。

這樣的查詢的正確索引是什麼?

+0

您可以嘗試索引'tbl(user_id,name,count)',但是MySQL可能仍然會選擇爲'group by'進行文件排序。 –

+0

@GordonLinoff兩者看起來都差不多(這太慢了)。奇怪的是沒有'USE INDEX',他使用'tbl(name)',這表現更糟糕。還有什麼我可以做的嗎? – Noam

+1

你能爲我們提供一個創建表結構嗎? –

回答

1

下可能會提高你的表現:

select t.name, 
     (select sum(counter) from tbl t2 where t2.name = t.name) as sumcounter 
from (select distinct name 
     from tbl 
     where user_id IN (".$user_ids.") 
    ) t 
order by sumcounter desc; 

現在把指標上tbl(user_id, name)tbl(name, counter)

如果這樣做,這是因爲內部子查詢使用第一個索引來獲取不同的名稱。 select中的嵌套子查詢將使用第二個索引來計算計數。

我不喜歡重寫這樣的查詢。有時可能需要獲得所需的性能。

+0

我鼓起勇氣修復了錯別字 –

1

正確的索引是IDX(user_id, name, counter),但在從索引獲取數據後,查詢需要額外的計算。如果不同名稱的數量大約爲10,那麼幾乎沒有任何事情可以做(大部分時間是由總和操作獲得的),但是如果有許多不同的名稱,則可以通過使用關於SUM(counter)的一些經驗知識來減少排序閾值:

SELECT name 
FROM tbl 
WHERE user_id IN (".$user_ids.") 
GROUP BY name 
HAVING SUM(counter) > 1000 -- adjust the threshold 
ORDER BY SUM(counter) DESC LIMIT 10 

UPD1。嗯,如果你說你已經試過了IDX(user_id, name, counter)索引並且性能是相同的,我實際上看不到它慢的原因,除非你傳遞了幾百個用戶ID(在這種情況下,這個時間用於查詢解析而不是執行)。

UPD2。 MySQL的IN運營商做一些額外的魔法:

返回1如果expr等於任何IN列表中的值,否則如果所有的值都是常數,它們的求返回0。根據expr的類型和排序。然後搜索該項目是使用二進制搜索完成的。

如果你通過INT值到操作IN (1,2,3)這意味着,如果你序列化存儲爲字符串IN ('1', '11', '111', '12')他們在字典順序排序整數它們被分類爲INTS。排序的基本原理是消除隨機索引讀取,這在將大量值傳遞給運算符時非常重要。

+0

我實際上傳遞了5k用戶標識符,而且我明白這可能需要一些時間,但5-6秒聽起來太過分了。 – Noam

+0

@Noam,在這種情況下,你有一個很長的查詢,它真的需要時間來解析查詢。你如何獲得ID?讓它們使用另一個表的JOIN可能會更好,是否有選項? – newtover

+0

這是可能的。你認爲5秒的主要部分可以解析5k user_ids? – Noam