当大量返回列时查询速度慢

时间:2013-04-04 11:37:31

标签: php mysql performance

我有一个相当大的查询,从php脚本中执行大约0.11秒(不是很大,但可能会被大量使用),但是当通过phpmyadmin执行时它声称查询花费了0.0047秒。

我做了一些调查,我发现如果我将返回的列减少到最小(从~50减少到2,留下那些用于对结果进行排序),查询速度要快得多(在同一个范围内)区域为phpmyadmin声称)。不幸的是,我需要这些列中的数据。如果导出返回的数据,则只有大约3k(作为CSV)。删除order by子句也可以忽略不计。

返回的行数非常少(约为8),因此phpmyadmin默认使用的limit子句不是一个因素。

检索到的数据量对查询产生了如此巨大的影响(而不是获取任何数据行),这似乎很奇怪。

我不确定如何在不减少返回列的情况下提高性能,或者将查询拆分(这只会改变瓶颈)。

查询如下(不确定这对明显的问题有多大帮助)。

SELECT DISTINCT
    f.MultiItemService,
    b.GroupName,
    n.CourierName,
    f.DeliveryserviceId,
    f.CourierId,
    f.DeliveryserviceName,
    f.CustomerDescription,
    f.SageCode,
    d.MarkupId,
    d.Description AS MarkupDescription,
    d.Discount,
    d.FixedAmount,
    f.Status,
    f.MinItems,
    f.MaxItems,
    f.MinWeight,
    f.MaxWeight,
    f.MinVolume,
    f.MaxVolume,
    f.MinWidth,
    f.MaxWidth,
    f.MinHeight,
    f.MaxHeight,
    f.MinDepth,
    f.MaxDepth,
    f.MinWorth,
    f.MaxWorth,
    d.MinItems AS MarkupMinItems,
    d.MaxItems AS MarkupMaxItems,
    d.MinWeight AS MarkupMinWeight,
    d.MaxWeight AS MarkupMaxWeight,
    d.MinVolume AS MarkupMinVolume,
    d.MaxVolume AS MarkupMaxVolume,
    d.MinWidth AS MarkupMinWidth,
    d.MaxWidth AS MarkupMaxWidth,
    d.MinHeight AS MarkupMinHeight,
    d.MaxHeight AS MarkupMaxHeight,
    d.MinDepth AS MarkupMinDepth,
    d.MaxDepth AS MarkupMaxDepth,
    d.MinWorth AS MarkupMinWorth,
    d.MaxWorth AS MarkupMaxWorth,
    f.Price,
    f.AdditionalPrice,
    f.DeliveryType,
    f.Consolidation,
    f.Surcharge,
    h.VatRate,
    k.InsuranceLevelId,
    k.InsuranceLevelDescription,
    k.InsuranceLevelMinimum,
    k.InsuranceLevelMaximum,
    m.OptionId, 
    m.OptionDescription, 
    m.OptionCost,
    f.CalculationId,
    CASE b.GroupName  WHEN 'home' THEN 1   WHEN 'customer' THEN 2  WHEN 'Standard' THEN 3 ELSE 4 END AS Priority
FROM users a
INNER JOIN groups b ON a.UserId = b.UserId AND a.ApiKey = 'ABC123ABC' 
INNER JOIN markups d ON b.GroupId = d.GroupId
INNER JOIN markups_deliveryservices o ON d.MarkupId = o.MarkupId 
INNER JOIN deliveryservices f ON o.DeliveryserviceId = f.DeliveryserviceId
INNER JOIN deliveryservices_days j ON f.DeliveryserviceId = j.DeliveryserviceId 
INNER JOIN couriers n ON f.CourierId = n.CourierId  
LEFT OUTER JOIN insurance_levels k ON f.DeliveryserviceId = k.DeliveryserviceId 
LEFT OUTER JOIN optional_services_deliveryservices l ON f.DeliveryserviceId = l.DeliveryserviceId 
LEFT OUTER JOIN optional_services m ON l.OptionId = m.OptionId  
INNER JOIN deliveryservices_delivery_groups g ON f.DeliveryserviceId = g.DeliveryserviceId
INNER JOIN delivery_groups h ON g.DeliveryGroupId = h.DeliveryGroupId 
INNER JOIN markups_delivery_groups p ON d.MarkupId = p.MarkupId
INNER JOIN delivery_groups q ON p.DeliveryGroupId = q.DeliveryGroupId 
WHERE h.DeliveryGroupId IN (15,12)
AND q.DeliveryGroupId IN (15,12) AND f.DeliveryType = 'Business And Domestic'
AND a.Status = 1
AND b.Status = 1
AND d.Status = 1
AND n.Status = 1
AND f.Status = 1
AND 1 BETWEEN f.MinItems AND f.MaxItems 
AND ((f.MultiItemService = 0 /* single parcel delivery services */
AND 32 BETWEEN f.MinWeight AND f.MaxWeight  /* item total weight within limits */
AND 193960 BETWEEN f.MinVolume AND f.MaxVolume  /* item total volume within limits */
AND 0 BETWEEN f.MinWorth AND f.MaxWorth)  /* item total value within limits */
OR (f.MultiItemService = 1 /* multiple parcel delivery services */
AND 0.16 BETWEEN f.MinWeight AND f.MaxWeight   /* max weight of any item within limits */
AND 969.8 BETWEEN f.MinVolume AND f.MaxVolume   /* max volume of any item within limits */
AND 0 BETWEEN f.MinWorth AND f.MaxWorth   /* max value of any item within limits */
AND ((32 != 0 AND 32 NOT BETWEEN f.MinWeight AND f.MaxWeight)  /* item total weight NOT within limits */
OR (149200 != 0 AND 193960 NOT BETWEEN f.MinVolume AND f.MaxVolume)  /* item total volume NOT within limits */
OR (0 != 0 AND 0 NOT BETWEEN f.MinWorth AND f.MaxWorth)
)))
AND 18.4 BETWEEN f.MinWidth AND f.MaxWidth 
AND 15.6 BETWEEN f.MinHeight AND f.MaxHeight 
AND 2.6 BETWEEN f.MinDepth AND f.MaxDepth
AND 1 BETWEEN d.MinItems AND d.MaxItems 
AND 32 BETWEEN d.MinWeight AND d.MaxWeight 
AND 193960 BETWEEN d.MinVolume AND d.MaxVolume 
AND 18.4 BETWEEN d.MinWidth AND d.MaxWidth 
AND 15.6 BETWEEN d.MinHeight AND d.MaxHeight 
AND 2.6 BETWEEN d.MinDepth AND d.MaxDepth
AND 0 BETWEEN d.MinWorth AND d.MaxWorth
AND j.DayOfWeek = 5 
AND j.UpToTime >= 61063
ORDER BY Priority

解释如下: -

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra 
1   SIMPLE  k   system  DeliveryserviceId   NULL    NULL    NULL    0   const row not found
1   SIMPLE  l   system  RuleId  NULL    NULL    NULL    0   const row not found
1   SIMPLE  m   system  PRIMARY NULL    NULL    NULL    0   const row not found
1   SIMPLE  a   ref PRIMARY,ApiKey  ApiKey  257 const   1   Using where; Using temporary; Using filesort
1   SIMPLE  q   index   PRIMARY PRIMARY 4   NULL    7   Using where; Using index; Using join buffer
1   SIMPLE  p   ref PRIMARY,DeliveryGroupId,MarkupId    DeliveryGroupId 4   DeliveryApiAdv.q.DeliveryGroupId    2    
1   SIMPLE  d   eq_ref  PRIMARY,GroupId PRIMARY 4   DeliveryApiAdv.p.MarkupId   1   Using where
1   SIMPLE  b   eq_ref  PRIMARY,UserId  PRIMARY 4   DeliveryApiAdv.d.GroupId    1   Using where
1   SIMPLE  o   ref markup_id,DeliveryserviceId markup_id   4   DeliveryApiAdv.p.MarkupId   2   Using index
1   SIMPLE  g   ref PRIMARY,DeliveryGroupId,DeliveryserviceId   PRIMARY 4   DeliveryApiAdv.o.DeliveryserviceId  1   Using where; Using index
1   SIMPLE  h   eq_ref  PRIMARY PRIMARY 4   DeliveryApiAdv.g.DeliveryGroupId    1    
1   SIMPLE  j   eq_ref  PRIMARY PRIMARY 5   DeliveryApiAdv.o.DeliveryserviceId,const    1   Using where
1   SIMPLE  f   eq_ref  PRIMARY,CourierId   PRIMARY 4   DeliveryApiAdv.o.DeliveryserviceId  1   Using where
1   SIMPLE  n   eq_ref  PRIMARY PRIMARY 4   DeliveryApiAdv.f.CourierId  1   Using where

编辑 - 经过更多调查后进行跟进。减少返回的列数可将运行时间大幅减少约95%。发生这种情况的点因使用的列而异(即,大约有12个INT列,但只有大约8个合理大小的VARCHAR列)。对于实验,我使用CONCAT_WS运行SQL以将返回的列连接在一起,而不是单独返回。这增加了从11到22返回的数据列数。

对于一些进一步的调试,我已经更改了主SELECT,因此它只返回其他表的ID字段,但根本没有更改连接。然后我用它来填充临时表。这很快就会运行。然后我加入了临时表,以获取我实际需要的数据。这个查询在主键上有非常简单的连接,没有where子句,但运行缓慢。

对我来说,这证实问题不在于查询的复杂性,而在于我遇到的与行结果大小相关的一些限制。

编辑 - 经过进一步调查后,问题似乎并未出现在查询中。在稍微不同配置的数据库上运行它会导致这不是问题。

1 个答案:

答案 0 :(得分:0)

您可以尝试在连接条件中使用where子句的某些部分。

e.g。

select a.*, b.* from a inner join b on a.col1 = b.col1 and b.status = 1 and a.status = 1