[英]How to speed up a complex SQL query with JOINs
I am using the following query, and as you can see via the results below, it is taking 2+ hours to return the results. 我正在使用以下查询,正如您通过下面的结果所看到的,返回结果需要2个多小时。 I'd love to know how I could speed this up -- I believe I've already built the indexes correctly but it is still taking an incredible amount of time to return this data. 我很想知道如何加快速度 - 我相信我已经正确地构建了索引但是仍然需要花费大量的时间来返回这些数据。
EXPLAIN SELECT Import_Values.base_vehicle_id,
Import_Values.qty,
Import_Values.part_type_id,
Import_Values.part_id,
Import_Values.position_id,
Import_Values.note,
Parts.partterminologyname,
BaseVehicle.YearID,
Make.MakeName,
Model.modelname,
SubModel.SubModelName,
EngineDesignation.EngineDesignationName,
EngineVIN.EngineVINName,
EngineBase.Liter,
EngineBase.CC,
EngineBase.CID,
EngineBase.Cylinders,
EngineBase.BlockType,
EngineBase.EngBoreIn,
EngineBase.EngBoreMetric,
EngineBase.EngStrokeIn,
EngineBase.EngStrokeMetric,
FuelDeliveryType.FuelDeliveryTypeName,
FuelDeliverySubType.FuelDeliverySubTypeName,
FuelSystemControlType.FuelSystemControlTypeName,
FuelSystemDesign.FuelSystemDesignName,
Aspiration.AspirationName,
CylinderHeadType.CylinderHeadTypeName,
FuelType.FuelTypeName,
IgnitionSystemType.IgnitionSystemTypeName,
Mfr.MfrName,
EngineVersion.EngineVersion,
Valves.ValvesPerEngine,
BedLength.BedLength,
BedLength.BedLengthMetric,
BedType.BedTypeName
FROM
Import_Values
INNER JOIN BaseVehicle
ON Import_Values.base_vehicle_id=BaseVehicle.BaseVehicleID
INNER JOIN Parts
ON Import_Values.part_type_id=Parts.PartTerminologyID
INNER JOIN Make
ON BaseVehicle.MakeID=Make.MakeID
INNER JOIN Model
ON BaseVehicle.ModelID=Model.ModelID
INNER JOIN Vehicle
ON Import_Values.base_vehicle_id=Vehicle.BaseVehicleID
INNER JOIN SubModel
ON Vehicle.SubModelID=SubModel.SubModelID
INNER JOIN VehicleConfig
ON Vehicle.VehicleID=VehicleConfig.VehicleID
INNER JOIN EngineConfig
ON VehicleConfig.EngineConfigID=EngineConfig.EngineConfigID
INNER JOIN EngineDesignation
ON EngineConfig.EngineDesignationID=EngineDesignation.EngineDesignationID
INNER JOIN EngineVIN
ON EngineConfig.EngineVINID=EngineVIN.EngineVINID
INNER JOIN EngineBase
ON EngineConfig.EngineBaseID=EngineBase.EngineBaseID
INNER JOIN FuelDeliveryConfig
ON EngineConfig.FuelDeliveryConfigID=FuelDeliveryConfig.FuelDeliveryConfigID
INNER JOIN FuelDeliveryType
ON FuelDeliveryConfig.FuelDeliveryTypeID=FuelDeliveryType.FuelDeliveryTypeID
INNER JOIN FuelDeliverySubType
ON FuelDeliveryConfig.FuelDeliverySubTypeID=FuelDeliverySubType.FuelDeliverySubTypeID
INNER JOIN FuelSystemControlType
ON FuelDeliveryConfig.FuelSystemControlTypeID=FuelSystemControlType.FuelSystemControlTypeID
INNER JOIN FuelSystemDesign
ON FuelDeliveryConfig.FuelSystemDesignID=FuelSystemDesign.FuelSystemDesignID
INNER JOIN Aspiration
ON EngineConfig.AspirationID=Aspiration.AspirationID
INNER JOIN CylinderHeadType
ON EngineConfig.CylinderHeadTypeID=CylinderHeadType.CylinderHeadTypeID
INNER JOIN FuelType
ON EngineConfig.FuelTypeID=FuelType.FuelTypeID
INNER JOIN IgnitionSystemType
ON EngineConfig.IgnitionSystemTypeID=IgnitionSystemType.IgnitionSystemTypeID
INNER JOIN Mfr
ON EngineConfig.EngineMfrID=Mfr.MfrID
INNER JOIN EngineVersion
ON EngineConfig.EngineVersionID=EngineVersion.EngineVersionID
INNER JOIN Valves
ON EngineConfig.ValvesID=Valves.Valvesid
INNER JOIN BedConfig
ON VehicleConfig.BedConfigID=BedConfig.BedConfigID
INNER JOIN BedLength
ON BedConfig.BedLengthID=BedLength.BedLengthID
INNER JOIN BedType
ON BedConfig.BedTypeID=BedType.BedTypeID
And the results... 结果......
+----+-------------+-----------------------+--------+-----------------------+---------+---------+-----------------------------------------------------------------+--------+-------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------------+--------+-----------------------+---------+---------+-----------------------------------------------------------------+--------+-------------------+
| 1 | SIMPLE | VehicleConfig | ALL | NULL | NULL | NULL | NULL | 171375 | |
| 1 | SIMPLE | Import_Values | ALL | base_vehicle_id | NULL | NULL | NULL | 18933 | Using join buffer |
| 1 | SIMPLE | BedConfig | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.VehicleConfig.BedConfigID | 1 | |
| 1 | SIMPLE | BedType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.BedConfig.BedTypeID | 1 | |
| 1 | SIMPLE | BedLength | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.BedConfig.BedLengthID | 1 | |
| 1 | SIMPLE | EngineConfig | eq_ref | PRIMARY,EngineBaseID | PRIMARY | 4 | icarcare_importfeeds.VehicleConfig.EngineConfigID | 1 | |
| 1 | SIMPLE | FuelType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.FuelTypeID | 1 | |
| 1 | SIMPLE | Valves | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.ValvesID | 1 | |
| 1 | SIMPLE | EngineVIN | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.EngineVINID | 1 | |
| 1 | SIMPLE | EngineVersion | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.EngineVersionID | 1 | |
| 1 | SIMPLE | IgnitionSystemType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.IgnitionSystemTypeID | 1 | |
| 1 | SIMPLE | Mfr | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.EngineMfrID | 1 | |
| 1 | SIMPLE | Aspiration | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.AspirationID | 1 | |
| 1 | SIMPLE | FuelDeliveryConfig | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.FuelDeliveryConfigID | 1 | |
| 1 | SIMPLE | FuelSystemDesign | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.FuelDeliveryConfig.FuelSystemDesignID | 1 | |
| 1 | SIMPLE | FuelDeliverySubType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.FuelDeliveryConfig.FuelDeliverySubTypeID | 1 | |
| 1 | SIMPLE | EngineDesignation | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.EngineDesignationID | 1 | |
| 1 | SIMPLE | EngineBase | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.EngineBaseID | 1 | |
| 1 | SIMPLE | CylinderHeadType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.EngineConfig.CylinderHeadTypeID | 1 | |
| 1 | SIMPLE | Parts | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.Import_Values.part_type_id | 1 | Using where |
| 1 | SIMPLE | FuelSystemControlType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.FuelDeliveryConfig.FuelSystemControlTypeID | 1 | |
| 1 | SIMPLE | BaseVehicle | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.Import_Values.base_vehicle_id | 1 | Using where |
| 1 | SIMPLE | Make | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.BaseVehicle.MakeID | 1 | |
| 1 | SIMPLE | Model | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.BaseVehicle.ModelID | 1 | |
| 1 | SIMPLE | Vehicle | eq_ref | PRIMARY,BaseVehicleID | PRIMARY | 4 | icarcare_importfeeds.VehicleConfig.VehicleID | 1 | Using where |
| 1 | SIMPLE | SubModel | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.Vehicle.SubModelID | 1 | |
| 1 | SIMPLE | FuelDeliveryType | eq_ref | PRIMARY | PRIMARY | 4 | icarcare_importfeeds.FuelDeliveryConfig.FuelDeliveryTypeID | 1 | |
+----+-------------+-----------------------+--------+-----------------------+---------+---------+-----------------------------------------------------------------+--------+-------------------+
27 rows in set (2 hours 39 min 30.51 sec)
Is there anything I can do? 有什么我能做的吗? I've tried analyzing the tables, optimizing them, etc. It just seems there must be more to do than let it run for 2 hours, lol. 我试过分析表格,优化它们等等。似乎必须要做的事情要比让它运行2小时更多,哈哈。
Are you sure that your query is logically correct? 您确定您的查询在逻辑上是正确的吗? I know nothing about your database so I can't tell what's going on, but here is a tiny bit of the query that seems to go against the pattern set down by the rest of the lookups in the query. 我对你的数据库一无所知,所以我不知道发生了什么,但这里有一小部分查询似乎违反了查询中其余查找设置的模式。
Here it is: 这里是:
INNER JOIN Vehicle
ON Import_Values.base_vehicle_id=Vehicle.BaseVehicleID
This looks strange, given the rest of your query. 鉴于您的其余查询,这看起来很奇怪。 I would have expected the ON condition to be looking up one entry in Vehicle based on Vehicle.VehicleID which is probably the indexed field in that table. 我本来期望ON条件是基于Vehicle.VehicleID在Vehicle中查找一个条目,这可能是该表中的索引字段。 Instead, it's looking it up based on Vehicle.BaseVehicleID which is probably an unindexed field. 相反,它正在根据Vehicle.BaseVehicleID查找它,这可能是一个未编入索引的字段。
It's entirely possible that this is logically correct. 完全有可能这在逻辑上是正确的。 But if it's logically incorrect, it could explain the long delay. 但如果它在逻辑上不正确,它可以解释长时间的延迟。 If this turns out to be the case, then your query will deliver wrong results in addition to taking too long to complete. 如果情况确实如此,那么除了花费太长时间完成之外,您的查询将提供错误的结果。
Edit: 编辑:
What is the relationship between ImportValues, Vehicle, and VehicleConfig? ImportValues,Vehicle和VehicleConfig之间有什么关系? Are there many VehicleConfigs for one Vehicle? 一辆车有很多VehicleConfigs吗? Or is it vice versa? 或反之亦然?
It looks like The explain plan ended up using a plan that amounts to a Cartesian Join between ImportValues and VehicleConfig. 看起来解释计划最终使用的计划相当于ImportValues和VehicleConfig之间的笛卡尔连接。 It's very unusual for a Cartesian join to produce the desired results. 笛卡尔联合产生预期结果是非常不寻常的。 In any event, you can expect a Cartesian Join to take a long, long time. 无论如何,你可以期待笛卡尔式加入需要很长时间。 Even if the query doesn't yield a Cartesian join, if the plan uses the same algorithm, it's still going to take a long, long time. 即使查询没有产生笛卡尔连接,如果计划使用相同的算法,它仍然需要很长很长时间。
Second Edit: 第二编辑:
There is another join condition that looks anomalous. 还有另一个看似异常的连接条件。 It's the join condition under VehicleConfig. 这是VehicleConfig下的连接条件。
INNER JOIN VehicleConfig
ON Vehicle.VehicleID=VehicleConfig.VehicleID
This is a pretty good join condition, but not for the VehicleConfig table. 这是一个非常好的连接条件,但不适用于VehicleConfig表。 What it says, in plain English, is that VehicleConfig determines Vehicle. 用简单的英语说,它说的是VehicleConfig确定车辆。 Not terribly surprising given the names of the tables and columns. 考虑到表格和列的名称,这并不奇怪。
But what determines VehicleConfig? 但是什么决定了VehicleConfig?
If the answer is "nothing", then it's no wonder that poor old MySQL is doing a full scan on the table, in addition to a full scan on Import_Values. 如果答案是“什么都没有”,那么除了在Import_Values上进行全面扫描之外,糟糕的旧MySQL正在对表进行全面扫描也就不足为奇了。
My conclusion: fix your query so that 26 of the 27 tables are determined by an FK that references the table's PK. 我的结论是:修复你的查询,以便27个表中的26个由引用表的PK的FK确定。 24 of your joins already meet this criterion. 您的24个联接已经符合此标准。 When you are finally done, and you do an explain plan, you should see only one table doing a full table scan, namely the Import_Values table. 当您最终完成并执行解释计划时,您应该只看到一个表执行全表扫描,即Import_Values表。
It should run faster, and it's more likely to produce correct results. 它应该运行得更快,并且更有可能产生正确的结果。
In looking at your explain plan, it looks like it's fine for everything except for Import_Values and VehicleConfig. 在查看您的解释计划时,除了Import_Values和VehicleConfig之外,它看起来很好。 It doesn't even look like it is considering any keys for VehicleConfig and just going straight into a full table scan which is bad. 它甚至看起来都没有考虑到VehicleConfig的任何键,只是直接进入全表扫描,这是不好的。
Try creating a composite index for VehicleConfig on VehicleID and BedConfigID and an index for Import_Values on base_vehicle_id and part_type_id. 尝试在VehicleID和BedConfigID上为VehicleConfig创建复合索引,在base_vehicle_id和part_type_id上为Import_Values创建索引。 If they already have indexes, it may just be a matter of updating statistics which you can do with the following commands. 如果它们已经有索引,则可能只需要更新可以使用以下命令执行的统计信息。 Hope that helps. 希望有所帮助。
ANALYZE TABLE VehicleConfig
ANALYZE TABLE Import_Values
请在执行此查询时尝试应用执行计划,并查看是否未建议任何缺少的键/索引。
Try to set forceplan on and run the query. 尝试设置forceplan并运行查询。 Another suggesstion is to break the query into multiple queries with 6-7 table joins in each query. 另一个建议是将查询分解为多个查询,每个查询中有6-7个表连接。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.