如何通过查询设置连接和分组的索引

时间:2011-04-05 13:07:07

标签: mysql join indexing

假设我们有一个如下所示的共同联接:

EXPLAIN SELECT *  
FROM visited_links vl
JOIN device_tracker dt ON ( dt.Client_id = vl.Client_id
AND dt.Device_id = vl.Device_id ) 
GROUP BY dt.id

如果我们执行解释,它会说:

id  select_type   table   type    possible_keys           key        key_len   ref                         rows   Extra
1   SIMPLE        vl      index   NULL                    vl_id      273       NULL                        1977   Using index; Using temporary; Using filesort
1   SIMPLE        dt      ref     Device_id,Device_id_2   Device_id  257       datumprotect.vl.device_id   4      Using where

我知道有时在使用group by时很难选择正确的索引但是,我可以设置哪些索引来避免在此查询中“使用临时,使用filesort”?为什么会这样?特别是,为什么在使用索引后会发生这种情况?

2 个答案:

答案 0 :(得分:2)

要提到的一点是,select(本例中为*)返回的字段应该在GROUP BY子句中,或者使用诸如SUM()或MAX()之类的agregate函数。否则可能会出现意外结果。这是因为如果没有告诉数据库如何选择不在group by子句中的字段,那么您可以获得该组的任何成员,几乎是随机的。


我看待它的方法是将查询分解为位。

  1. 你有一个连接(dt.Client_id = vl.Client_id和dt.Device_id = vl.Device_id)所以所有这些字段都应该在它们各自的表中编入索引。

  2. 您正在使用GROUP BY dt.id,因此您需要一个包含dt.id的索引

  3. 但是...

    (dt.client_id,dt.device_id,dt.id)上的索引不适用于GROUP BY

    (dt.id,dt.client_id,dt.device_id)上的索引不适用于连接。

    有时您最终会得到一个无法使用索引的查询。

    另见: http://ntsrikanth.blogspot.com/2007/11/sql-query-order-of-execution.html

答案 1 :(得分:0)

您没有发布您的索引,但首先,您需要在(client_id, device_id)上设置visited_links的索引,并在(client_id, device_id, id)上设置device_tracker的索引确保查询完全编入索引。

来自优秀高性能MySQL,第2版的第191页:

  

当MySQL无法使用索引时,它有两种GROUP BY策略:它可以使用临时表或文件集来执行分组。根据查询,任何一个都可以更高效。您可以强制优化器使用SQL_BIG_RESULT和SQL_SMALL_RESULT优化器提示选择一个方法或另一个方法。

在您的情况下,我认为问题源于加入多个列并一起使用GROUP BY,即使建议的索引到位后也是如此。如果删除(a)其中一个连接条件或(b)GROUP BY,则不需要文件排序。

但是,请记住,文件排序并不总是使用实际文件,如果结果集足够小,它也可能完全在内存缓冲区内发生,因此性能损失可能很小。考虑查询的挂钟时间。

HTH!