简体   繁体   中英

Slow query when large number of returned columns

I have a fairly large query that is taking about 0.11 seconds to execute from within a php script (not massive, but might be fairly heavily used), but when executed through phpmyadmin it claims the query is taking 0.0047 seconds.

I have done a fair bit of investigation and I have found that if I reduce the returned columns to a minimum (from ~50 down to 2, leaving those used to sort the results) the query is far faster (within the same region as phpmyadmin claims). Unfortunately I need the data in those columns. If the returned data is exported it is only around 3k (as a CSV). Removing the order by clause also has negligible effect.

The number of rows returned is quite small (about 8), so the limit clause that phpmyadmin uses by default is not a factor.

It seems strange that the amount of data retrieved is having such a dramatic effect on the query (rather than on the fetch of any row of data).

I am unsure what I can do to improve performance without reducing the returned columns, or splitting the query up (which would just change the bottleneck).

The query is as follows (not sure how much help this is with the apparent problem).

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

And the explain is as follows:-

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

EDIT - Bit of a follow up after more investigation. Cutting down the number of columns returned reduces the run time dramatically by about 95%. The point where this happens varies on what columns are used (ie, about a dozen INT columns, but only about 8 reasonable size VARCHAR columns). For an experiment I ran the SQL using CONCAT_WS to join the returned columns together rather than being individually returned. This increased the number of columns worth of data returned from 11 to 22.

For some further debugging I have changed the main SELECT so it just returns the ID fields of the other tables, but with no change to the joins at all. I have then used this to populate a temp table. This runs rapidly. I have then joined that temp table back against the tables to get the data I actually need. This query has very simple joins on the primary keys and no where clause, but runs slowly.

To me this confirms the problem is not the complexity of the query, rather some limit I am hitting related to the resulting row size.

EDIT - after further investigation the problem does not appear to be down to the query. Running it on a slightly differently configured database results in this not being an issue.

You may try to use some part of where clause in the join condition.

eg

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM