0

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

            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,所以我想我做错了什么。

这种查询的正确索引是什么?

4

2 回答 2

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将使用第二个索引来计算计数。

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

于 2013-08-25T13:19:59.010 回答
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运算符还有一些额外的魔力:

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

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

于 2013-08-26T03:34:37.060 回答