2013-04-04 3 views
0

У меня довольно большой запрос, который занимает около 0,11 секунды для выполнения из PHP-скрипта (не массивный, но может быть довольно сильно использован), но при выполнении через phpmyadmin он утверждает, что запрос занимает 0.0047 секунды.Медленный запрос при большом количестве возвращенных столбцов

Я проверил справедливое расследование, и я обнаружил, что если я уменьшу возвращаемые столбцы до минимума (от ~ 50 до 2, оставив те, которые используются для сортировки результатов), запрос выполняется намного быстрее (в пределах тот же регион, что и phpmyadmin). К сожалению, мне нужны данные в этих столбцах. Если возвращенные данные экспортируются, это всего около 3k (как CSV). Удаление предложения order by также имеет незначительный эффект.

Число возвращенных строк довольно мало (около 8), поэтому предложение limit, которое использует 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 

EDIT - Bit из последующих после более расследования. Сокращение количества возвращенных столбцов значительно сокращает время выполнения примерно на 95%. Точка, в которой это происходит, зависит от того, какие столбцы используются (т. Е. Около десятка столбцов INT, но только около 8 разумных размеров столбцов VARCHAR). Для эксперимента я запустил SQL, используя CONCAT_WS, чтобы присоединиться к возвращенным столбцам вместе, а не возвращаться индивидуально. Это увеличило количество столбцов данных, возвращенных с 11 по 22.

Для некоторой дополнительной отладки я изменил основной SELECT, чтобы он просто возвращал поля идентификатора других таблиц, но без каких-либо изменений в соединениях , Затем я использовал это для заполнения таблицы temp. Это быстро работает. Затем я подключился к этой временной таблице обратно к таблицам, чтобы получить нужные мне данные. Этот запрос имеет очень простые сочетания в первичных ключах и без предложения where, но работает медленно.

Для меня это подтверждает, что проблема заключается не в сложности запроса, а в некотором ограничении, которое я нахожу, связанного с полученным размером строки.

EDIT - после дальнейшего расследования проблема не соответствует запросу. Запуск его в слегка отличающейся конфигурации приводит к тому, что это не проблема.

+0

PHPMYADMIN обычно ставит условие LIMIT на запрос выполнен для того, чтобы разбиваться результаты, которые он возвращается к вам - то есть, скорее всего, причина, по которой PHPMYADMIN выполняет запрос намного быстрее. – alexpls

+0

Спасибо. Следует упомянуть, что этот запрос возвращает только небольшое количество строк (8 с помощью парм в одной кавычки), что находится в пределах ограничения, используемого phpmyadmin. Я отредактирую сообщение, чтобы сделать это более очевидным. – Kickstart

+0

Если у вас есть хотя бы одна большая таблица, упомянутая в вашем запросе, то проблема с вашим запросом заключается в том, что 'Join's. Вот та же проблема: http://forums.devshed.com/mysql-help-4/slow-query-in-php-script-fast-in-phpmyadmin-why-124739.html –

ответ

0

Вы можете попытаться использовать часть предложения where в условии соединения.

например.

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

Спасибо. Не уверен, что это повлияет на производительность. Дайте ему попробовать разницу в пределах экспериментальной ошибки, в то время как объяснение идентично, и время при включении профилирования практически одинаково. Проблема не является запросом. Скорее что-то связано с количеством возвращенных столбцов. – Kickstart

+0

Да, вы правы, но это поможет улучшить производительность. вы можете попробовать. – kwelsan

+0

Боюсь, я попробовал. Это не имеет никакого значения для времени выполнения запроса (иногда немного быстрее, иногда чуть медленнее). Я бы ожидал, что MySQL все равно будет оптимизировать такие вещи – Kickstart

Смежные вопросы