var1 IN(1,2,...)的正确索引GROUP BY(var2)ORDER BY SUM(var3)LIMIT X

时间:2013-08-25 12:48:26

标签: mysql sql query-optimization

我正在尝试优化以下查询:

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

Tbl info:name是VARCHAR,counter和user_id是INT。 user_id,名称是唯一的。

我尝试添加IDX(user_id, counter, name),但在EXPLAIN我仍然看到Using where; Using index; Using temporary; Using filesort所以我猜我做错了。

此类查询的正确索引是什么?

2 个答案:

答案 0 :(得分: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中的嵌套子查询将使用第二个索引来计算计数。

我不喜欢重写这样的查询。有时可能需要获得所需的性能。

答案 1 :(得分:1)

正确的索引是IDX(user_id, name, counter),但在从索引中获取数据后,查询需要额外的计算。如果不同名称的数量大约是10,那么你几乎无法做任何事情(大部分时间是由sum运算完成的),但是如果有许多不同的名称,你可以通过使用关于{的一些经验知识来减少排序。 {1}}门槛:

SUM(counter)

UPD1。嗯,如果你说你已经尝试了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 索引并且性能相同,我实际上看不出它为什么慢的原因,除非你通过几百个用户id(在这种情况下时间是用于查询解析而不用于执行)。

UPD2。 MySQL IN运算符会做一些额外的魔术:

  

如果expr等于IN列表中的任何值,则返回1,否则返回0.如果所有值都是常量,则根据expr的类型计算它们并进行排序。然后使用二分搜索完成对项目的搜索。

这意味着如果将INT值传递给运算符IDX(user_id, name, counter),它们将被排序为INTS,如果序列化存储为字符串IN (1,2,3)的整数,则它们按字典顺序排序。排序的基本原理是消除随机索引读取,这在将大量值传递给运算符时非常重要。