Пожалуйста, помогите мне с анализом медленных запросов Mysql - PullRequest
2 голосов
/ 13 января 2010

У меня есть этот запрос MySQL, который я пытаюсь проанализировать. Это очень медленно, таблица посещений составляет около 50 тыс. Записей, этот запрос никогда не возвращается. Когда я попробовал оператор объяснения, я обнаружил, что индекс не используется в таблице посетителей, несмотря на доступность индекса. Теперь это большая загадка, которую мне нужно решить. Любые намеки приветствуются.

Запрос:

select distinct
  visitor0_.ID as ID130_,      

  case when visitor0_1_.id is not null then 1 when
  visitor0_.ID is not null then 0
  end as clazz_

from Visitor visitor0_ 
left outer join Operator visitor0_1_ on visitor0_.ID=visitor0_1_.id
where (visitor0_.ID not in
    (select operator1_.id 
     from Operator operator1_ 
     inner join Visitor operator1_1_ on operator1_.id=operator1_1_.ID))
  and (exists 
    (select visitorpro2_.ID 
     from VisitorProfileField visitorpro2_, ProfileField profilefie3_ 
     where visitorpro2_.profileFieldID=profilefie3_.ID 
       and visitorpro2_.visitorID=visitor0_.ID 
       and profilefie3_.name='subscription86' 
       and visitorpro2_.numberVal=1 
       and visitorpro2_.stringVal='Manual'))

Объясните вывод снимка экрана: http://grab.by/grabs/9c3a629a25fc4e9ec0fa54355d4a092c.png

Ответы [ 2 ]

2 голосов
/ 13 января 2010

Из того, что я сделал из вашего запроса, следующее должно дать тот же результат, без подзапросов и с гораздо более высокой производительностью.

select v.ID as ID130_, 0 as clazz_
from Visitor v
left outer join (VisitorProfileField vpf join ProfileField pf 
                   on vpf.profileFieldID = pf.ID)
  on v.ID = vpf.visitorID and pf.name='subscription86' 
    and vpf.numberVal=1 and vpf.stringVal='Manual'
left outer join Operator o on v.ID = o.ID
where o.ID IS NULL;

Пожалуйста, объясните, если я что-то понял неправильно. Похоже, что ваш предикат NOT IN исключает любые идентификаторы Visitor, которые соответствуют любым идентификаторам в Operator. Таким образом, подзапрос генерирует список всех идентификаторов, которые находятся в обеих таблицах, поэтому условие NOT IN эквивалентно внешнему соединению Operator и простому тесту, где o.ID IS NULL.

Это означает, что выражение CASE в вашем списке выбора не имеет смысла, так как оно будет 0, если ваши условия соответствуют только Visitor строкам, которые не соответствуют ни одной строке в Operator.

Я думаю, что-то серьезно запуталось в вашем запросе.

Также кажется, что вы используете антипаттерн EAV в таблицах VisitorProfileField и ProfileField. Это доставит вам много хлопот.

1 голос
/ 13 января 2010

Ваш запрос ... большой. Можете ли вы объяснить, что это для вас? Похоже, что он тянет каждый идентификатор посетителя и независимо от того, являются ли они оператором, если они не являются оператором, и у них есть определенные настройки профиля. Это не имеет смысла, так что я, должно быть, что-то там упускаю.

Вот моя попытка, основанная на моем понимании того, что вы пытаетесь сделать:

select distinct visitor.ID, IF(operator.id IS NOT NULL, 1, 0) AS clazz
from Visitor left outer join Operator on visitor.ID = operator.id
where not exists 
    (select 'x' from Operator OperatorTwo where OperatorTwo.id = visitor.ID)
and exists
    (select 'x' from VisitorProfileField, ProfileField
        where VisitorProfileField.profileFieldID = ProfileField.ID
        and VisitorProfileField.profileFieldID.visitorID = visitor.ID
        and VisitorProfileField.profileFieldID.numberVal = 1
        and VisitorProfileField.profileFieldID.stringVal = 'Manual'
        and ProfileField .name = 'subscription86')

Объединенная таблица с именем "operator1_1_", по-видимому, не используется, ее можно удалить. Если вы используете его просто для того, чтобы удостовериться, что в этой таблице есть запись для посетителя, я бы использовал вместо существующего соединение «существует». Я бросил это.

Я переключил ваш not in на not not, который, я думаю, может быть проще оптимизировать для MySQL. Я использовал IF вместо случая, потому что у вас есть только два, и это было короче, чтобы напечатать. Я не знаю, является ли один из них быстрее / проще в MySQL.

Я могу вам сказать, что по моему опыту производительность MySQL умирает с подзапросами в запросах. Кажется, что отказывается от их оптимизации и начинает запускать их построчно. Могу поспорить, что если вы используете временную таблицу результатов (только для целей тестирования), ваш запрос будет выполняться намного быстрее.

Изменить:

Билл пошел дальше, чем я, я не зашел достаточно далеко. Мне нравится запрос Билла, и я согласен с его выводами об утверждении CASE, которое меня как-то сбило с толку.

...