2013-06-18 168 views
0

我有1表locations和4个相同的表:countriesregionsprovincescities为什么不使用索引键?

所有的表的InnoDB

的所有表有一个柱id其是主要,非空,无符号整型,汽车式增量

所有的表列name这非空,VARCHAR,默认'

locations持有外键的所有其他4张桌子。所有非空。

locations表还拥有一些其他的列和索引/键,但没有一个不相关的其他4台

现在我有这个疑问:

DESCRIBE SELECT * FROM locations 
LEFT JOIN cities ON locations.city_id = cities.id 
LEFT JOIN provinces ON locations.province_id = provinces.id 
LEFT JOIN regions ON locations.region_id = regions.id 
LEFT JOIN countries ON locations.country_id = countries.id 
WHERE locations.id > 1 

结果是让我满意的:所有表都使用他们的密钥。

1, SIMPLE, locations, range , PRIMARY, PRIMARY, 4,       , 393, Using where 
1, SIMPLE, cities , eq_ref, PRIMARY, PRIMARY, 4, mydb.locations.city_id , 1, 
1, SIMPLE, provinces, eq_ref, PRIMARY, PRIMARY, 4, mydb.locations.province_id, 1, 
1, SIMPLE, regions , eq_ref, PRIMARY, PRIMARY, 4, mydb.locations.region_id , 1, 
1, SIMPLE, countries, eq_ref, PRIMARY, PRIMARY, 4, mydb.locations.country_id , 1, 

问题

仅改变LEFT JOIN countriesINNER JOIN countries是确定的。所有表格仍然使用密钥。

1, SIMPLE, locations, range , PRIMARY,locations_country_id_fk, PRIMARY, 4,        , 341, Using where 
1, SIMPLE, cities , eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.city_id , 1, 
1, SIMPLE, provinces, eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.province_id, 1, 
1, SIMPLE, regions , eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.region_id , 1, 
1, SIMPLE, countries, eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.country_id , 1, 

只有LEFT JOIN provinces转变成INNER JOIN provinces是确定的。所有表格仍然使用密钥。

1, SIMPLE, locations, range , PRIMARY,locations_province_id_fk, PRIMARY, 4,        , 341, Using where 
1, SIMPLE, provinces, eq_ref, PRIMARY       , PRIMARY, 4, ftc_dev.locations.province_id, 1, 
1, SIMPLE, regions , eq_ref, PRIMARY       , PRIMARY, 4, ftc_dev.locations.region_id , 1, 
1, SIMPLE, countries, eq_ref, PRIMARY       , PRIMARY, 4, ftc_dev.locations.country_id , 1, 
1, SIMPLE, cities , eq_ref, PRIMARY       , PRIMARY, 4, ftc_dev.locations.city_id , 1, 

只有LEFT JOIN cities更改为INNER JOIN cities是确定的。所有表格仍然使用密钥。

1, SIMPLE, locations, range , PRIMARY,locations_city_id_fk, PRIMARY, 4,        , 341, Using where 
1, SIMPLE, provinces, eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.province_id, 1, 
1, SIMPLE, regions , eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.region_id , 1, 
1, SIMPLE, countries, eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.country_id , 1, 
1, SIMPLE, cities , eq_ref, PRIMARY      , PRIMARY, 4, ftc_dev.locations.city_id , 1, 

但只有LEFT JOIN regions变更为INNER JOIN regions确定。 regions表不使用其密钥。我得到这个:

1, SIMPLE, regions , ALL , PRIMARY      , null     , null, null      , 269, 
1, SIMPLE, locations, ref , PRIMARY,locations_region_id_fk, locations_region_id_fk, 5, mydb.regions.id   , 1, Using where 
1, SIMPLE, cities , eq_ref, PRIMARY      , PRIMARY    , 4, mydb.locations.city_id , 1, 
1, SIMPLE, provinces, eq_ref, PRIMARY      , PRIMARY    , 4, mydb.locations.province_id, 1, 
1, SIMPLE, countries, eq_ref, PRIMARY      , PRIMARY    , 4, mydb.locations.country_id , 1, 

这很奇怪!因为countriesregionsprovincescities是相同的,据我可以看到!但是这种行为证明4个表格是而不是相同(朝着locations表格)。我可能错过了什么让他们更加相同?

我已经看过SHOW TABLE STATUS LIKE 'table_name'DESCRIBE table_name。几乎所有东西都是一样的。并且在有变化的地方(例如rowsavg_row_length,data_length,auto_increment, create_time),regions表值始终位于其他值之间的某处。

编辑:为什么我要问:

查询与留在区JOIN时间约为350毫秒(时间:10毫秒,抓取:为340mS)。

INNER JOIN查询区域大约需要550ms。 (持续时间:330ms,取指:220ms)。

EDIT2(不完全了解,但猜测这与不能够缓存办?!):

查询与地区STRAIGHT_JOIN作为LEFT JOIN执行好,并提供与INNER JOIN相同的输出。这很好。但仍然没有帮助我回答为什么regions表的行为不同。我应该在哪里看到regions与其他3个看似相同的表格之间的实际差异。

+0

刚刚添加了一个编辑,指出INNER JOIN比LEFT JOIN减少 –

+0

正如@Kickstart指出的那样,除了INNER JOIN和LEFT JOIN,我还可以做STRAIGHT_JOIN。这似乎与INNER JOIN具有相同的输出,但性能与LEFT JOIN相同。这很好。但仍然需要弄清楚,为什么“地区”表。是什么让这张桌子如此不同? –

回答

0

我怀疑这是数据。

locations.id> 1可能根本没有多大用途来缩小数据范围,我怀疑位置有很多记录。

区域可能有较少的记录,所以使用这个作为主表来加入其他人可能更有效。也就是说,您的原始查询会将所有连接限制为393行,而您的第二个查询会将区域中只有269行的连接挂起。

如果您希望强制连接的顺序,则可以使用STRAIGHT_JOIN。

+0

'locations.id> 1'仅供参数使用;只是为了让“位置”表甚至使用一把钥匙。我不希望“地点”表格是相同的。只有4个“城市”,“地区”,“省份”和“国家”应该相同。 –

+0

您可能可以使用regions.id> 1来产生相同的效果,以强制索引在区域上使用。 MySQL只选择了记录数最少的表(以指定的关键字段)加入其他INNER JOINed表。 – Kickstart

+0

这是有趣的部分!例如“省份”表格甚至比“地区”表格少**记录。但对于那张桌子来说并不重要。 “城市”表比**地区表有更多的**记录。而对于那张桌子也没关系!所以它似乎与每个“相同”表内的行数没有关系。 –