假设我们有一个如下所示的共同联接:
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”?为什么会这样?特别是,为什么在使用索引后会发生这种情况?
答案 0 :(得分:2)
要提到的一点是,select(本例中为*)返回的字段应该在GROUP BY子句中,或者使用诸如SUM()或MAX()之类的agregate函数。否则可能会出现意外结果。这是因为如果没有告诉数据库如何选择不在group by子句中的字段,那么您可以获得该组的任何成员,几乎是随机的。
我看待它的方法是将查询分解为位。
你有一个连接(dt.Client_id = vl.Client_id和dt.Device_id = vl.Device_id)所以所有这些字段都应该在它们各自的表中编入索引。
您正在使用GROUP BY dt.id,因此您需要一个包含dt.id的索引
但是...
(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!