0

我有 1 个表locations和 4 个相同的表:countries, regions, provinces,cities

所有表都是 InnoDB

所有表都有一个列id,它是 Primary、Non-Null、Unsigned Int、Auto-Increment

所有表都有一列name是 Non-Null, VarChar, default ''

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 countries换成INNER 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, 

这很奇怪!因为countries, regions, provinces,cities就我所见是相同的!但是这种行为证明了 4 个表并不相同(朝向locations表)。为了使它们更加相同,我会错过什么?

我已经看过SHOW TABLE STATUS LIKE 'table_name'DESCRIBE table_name。几乎所有东西都是一样的。在有变化的地方(例如rows, avg_row_length, data_length, auto_increment, create_time),regions表格值总是介于其他值之间。

编辑:为什么我要问:

在区域上使用 LEFT JOIN 进行查询大约需要 350 毫秒(持续时间:10 毫秒,获取:340 毫秒)。

在区域上使用 INNER JOIN 进行查询大约需要 550 毫秒。(持续时间:330 毫秒,获取:220 毫秒)。

(不完全知道,但猜测这与无法缓存有关?!)

编辑2:

在区域上使用 STRAIGHT_JOIN 查询的性能与 LEFT JOIN 一样好,并提供与 INNER JOIN 相同的输出。这很好。但它仍然不能帮助我回答为什么regions表的行为不同。我应该在哪里发现regions与其他 3 个看似相同的表之间的实际差异。

4

1 回答 1

0

我怀疑这取决于数据。

location.id > 1 可能根本无法缩小数据范围,我怀疑位置有很多记录。

Regions 的记录可能较少,因此将其用作主表来连接其他表可能更有效。即,您的原始查询将所有连接挂起,将其限制为 393 行,而您的第二个查询将连接挂起仅 269 行的区域。

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

于 2013-06-18T13:01:02.137 回答