Пожалуйста, помогите мне с анализом медленных запросов Mysql
-
20-09-2019 - |
Вопрос
У меня есть этот запрос mysql, который я пытаюсь проанализировать.Это очень медленно, таблица посетителей здесь содержит около 50 тысяч записей, этот запрос никогда не возвращается.Когда я попробовал использовать инструкцию explain, я обнаружил, что индекс не используется в таблице visitor, несмотря на то, что индекс доступен.Теперь это великая головоломка, которую мне нужна помощь в решении.Любые намеки приветствуются.
Запрос:
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
Решение
Из того, что я делаю вывод из вашего запроса, следующее должно привести к тому же результату, без подзапросов и с намного более высокой производительностью.
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
таблицы.Это доставит вам много хлопот.
Другие советы
Ваш запрос таков...большой.Можете ли вы объяснить, что это дает вам?Похоже, что он извлекает идентификатор каждого посетителя и определяет, являются ли они операторами, если они не являются операторами и у них есть определенные настройки профиля.В этом нет ни капли смысла, так что, должно быть, я чего-то здесь не понимаю.
Вот моя попытка, основанная на моем понимании того, что вы пытаетесь сделать:
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_", похоже, не используется, вы должны быть в состоянии удалить это.Если вы используете его просто для того, чтобы убедиться, что в этой таблице есть запись для посетителя, я бы использовал exists вместо join .Я это уронил.
Я переключил ваш not на not exists, что, я думаю, может быть проще для MySQL оптимизировать.Я использовал IF вместо регистра, потому что у вас их всего два, и вводить их было короче.Я не знаю, работает ли какой-либо из них быстрее / проще в MySQL.
Я могу сказать вам, что, по моему опыту, производительность MySQL падает вместе с подзапросами в suqueries.Кажется, он отказывается от их оптимизации и начинает запускать их строка за строкой.Держу пари, что если бы вы использовали временную таблицу результатов (только для целей тестирования), вы бы обнаружили, что ваш запрос выполняется намного быстрее.
Редактировать:
Билл зашел дальше, чем я, а я зашел недостаточно далеко.Мне нравится вопрос Билла, и я согласен с его выводами по поводу изложения ДЕЛА, которые меня немного выбили из колеи.