2012-12-31 93 views
7
explain 
select 
    * 
from 
    zipcode_distances z 
inner join 
    venues v  
    on z.zipcode_to=v.zipcode 
inner join 
    events e 
    on v.id=e.venue_id 
where 
    z.zipcode_from='92108' and 
    z.distance <= 5 

我试图找到所有“在5英里的邮政编码92108以内的场地的活动”,但是,我很难优化此查询。如何避免在此mysql查询上进行全表扫描?

这是什么解释的样子:

id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra 

1, SIMPLE, e, ALL, idx_venue_id, , , , 60024, 
1, SIMPLE, v, eq_ref, PRIMARY,idx_zipcode, PRIMARY, 4, comedyworld.e.venue_id, 1, 
1, SIMPLE, z, ref, idx_zip_from_distance,idx_zip_to_distance,idx_zip_from_to, idx_zip_from_to, 30, const,comedyworld.v.zipcode, 1, Using where; Using index 

我得到的“E”表进行全表扫描,我想不出我需要创建得到它什么指数要快。

任何意见,将不胜感激

谢谢

+0

您是否需要结果集中所有表的所有列? –

+0

我试图避免使用“in”子查询。 – john

+0

我想要做的事情的俗语说明是找到邮政编码驻留在邮政编码中的场所,我发现邮政编码靠近92108.因此,它加入场地,然后加入与该场地相关的活动。 – john

回答

7

基于对你的问题EXPLAIN输出,你已经拥有的所有索引查询应该使用,即:

CREATE INDEX idx_zip_from_distance 
    ON zipcode_distances (zipcode_from, distance, zipcode_to); 
CREATE INDEX idx_zipcode ON venues (zipcode, id); 
CREATE INDEX idx_venue_id ON events (venue_id); 

(我不是从你的目录名称确定idx_zip_from_distance是否真的包括zipcode_to列,如果不是,你应该添加它使其成为covering index。另外,为了完整性,我还包含venues.ididx_zipcode,但假设它是表的主键并且使用InnoDB,将自动包含在内。)

但是,它看起来像MySQL正在选择一个不同的,可能不是最理想的查询计划,它扫描所有事件,查找他们的场所和邮政编码,然后才按距离过滤结果。如果事件表的基数足够低,这个可能是是最佳的查询计划,但是从你提出这个问题的事实,我认为它不是。为次佳的查询计划

一个原因可能是你有太多指标被混淆的策划者的事实。例如,你是否真的需要邮政编码表上所有这三个索引,因为它存储的数据可能是对称的?就个人而言,我只会建议我上面描述的索引,并在(zipcode_to, zipcode_from)上加上一个唯一的索引(如果您没有人工索引,也可以是主键)(最好按照该顺序进行,以便偶尔查询zipcode_to=?可以利用它)。然而,基于我做过的一些测试,我怀疑为什么MySQL选择错误的查询计划的主要问题来自于表的相对基数。据推测,您的实际zipcode_distances表是巨大的,而且MySQL不够聪明,无法完全了解WHERE条款中的条件是否真的缩小了它的范围。

如果是这样,最好的和简单的解决办法可能是简单地force MySQL to use the indexes you want

select 
    * 
from 
    zipcode_distances z 
    FORCE INDEX (idx_zip_from_distance) 
inner join 
    venues v  
    FORCE INDEX (idx_zipcode) 
    on z.zipcode_to=v.zipcode 
inner join 
    events e 
    FORCE INDEX (idx_venue_id) 
    on v.id=e.venue_id 
where 
    z.zipcode_from='92108' and 
    z.distance <= 5 

随着该查询,你确实应该得到理想的查询计划。 (你需要FORCE INDEX这里,因为只有USE INDEX查询规划仍然可以决定使用一个表扫描,而不是建议的指数,击败目的。我有这种情况发生时,我第一次测试这一点。)

诗篇。这是一个关于SQLize的演示,演示了这个问题withwithoutFORCE INDEX

0

你可以使用子查询:

select * from zipcode_distances z, venues v, events e 
where 
    z.id in (select id from zipcode z where z.zipcode_from='92108' and z.distance <= 5) 
    and z.zipcode_to=v.zipcode 
    and v.id=e.venue_id 
1

有两个表中的索引中的列?

e.id and v.venue_id 

如果您不这样做,请在两个表中创建索引。如果您已经有了,那么可能是因为您在一个或多个表中的记录数很少,并且分析器检测到执行全面扫描而不是索引读取效率更高。

0

您正在选择所有表(select *)中的所有列,因此,当查询引擎必须从索引到每个单一行上的表执行查找时,优化器中没有什么地方会使用索引。