0

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

我进行了相当多的调查,我发现如果我将返回的列减少到最低限度(从 ~50 减少到 2,留下用于对结果进行排序的列)查询会快得多(在与 phpmyadmin 相同的区域内索赔)。不幸的是,我需要这些列中的数据。如果返回的数据被导出,它只有 3k 左右(作为 CSV)。删除 order by 子句的影响也可以忽略不计。

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

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

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

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

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%。发生这种情况的点因使用的列而异(即,大约十几个 INT 列,但只有大约 8 个合理大小的 VARCHAR 列)。对于一个实验,我使用 CONCAT_WS 运行 SQL 以将返回的列连接在一起,而不是单独返回。这将返回的数据列数从 11 增加到 22。

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

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

编辑 - 经过进一步调查后,问题似乎与查询无关。在配置稍有不同的数据库上运行它不会成为问题。

4

1 回答 1

0

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

例如

select a.*, b.* from a inner join b on a.col1 = b.col1 and b.status = 1 and a.status = 1
于 2013-04-04T12:09:58.477 回答