В чем же причина ОГРОМНОГО замедления, более 60 секунд и часто не хватает времени?
Предложение IN
работает хорошо, когда данные в состоянии IN
является «малыми» и «детерминированным». Это потому, что условие оценивается один раз за строку. Итак, если предположить, что запрос в предложении IN
возвращает 100 строк, а таблица в предложении FROM
имеет 1000 строк, сервер должен будет выполнить 100 * 1000 = 100,000
сравнения, чтобы отфильтровать ваши данные. Слишком много усилий, чтобы фильтровать слишком мало данных, не так ли? Конечно, если ваши наборы данных (как в from
, так и в in
) больше, вы можете себе представить эффект.
Кстати, когда вы используете подзапрос как in
состоянии, есть также дополнительные накладные расходы: подзапрос должен быть выполнен раз для каждой строки.Таким образом, последовательность что-то вроде этого:
- строки 1
- выполнения подзапроса
- проверить, если значение строки 1 соответствует значению результата подзапроса
- , если это верно, сохранить строку в результирующем наборе; исключить это иначе
- строка 2
- выполнить подзапросы проверку
- если значение строки 2 соответствует значению результата подзапроса
- , если это верно, сохранить строку в набор результатов; исключающий его иначе
- ...
Слишком много работы, вы не думаете?
Есть ли способ, чтобы поместить результаты в переменную с запятыми?
Да, есть способ ... но вы бы действительно хотите сделать это? Давайте посмотрим:
Во-первых, создать список со значениями, которые вы хотите фильтровать:
set @valueList = (select group_concat(vendor_id separator ',')
from (select vendor_id from vendor_master where vendor_master_id = 12345) as a)
Затем создайте выражение SQL:
set @sql = concat('select * from invoices where vendor_id in (', @valueList, ')';
Наконец, создать подготовленное заявление и выполнить его :
prepare stmt from @sql;
execute stmt;
-- when you're done, don't forget to deallocate the statement:
-- deallocate prepare stmt;
Я снова спрашиваю вас: сделать вам действительно хотите все это сделать?
Или, чтобы получить внутреннее заявление, чтобы выполнить первый?
Все остальные ответы указывают вас в правильном направлении: вместо использования in
использования inner join
:
select i.*
from invoices as i
inner join (
select distinct vendor_id
from vendor_master
where vendor_master_id = 12345
) as vm on i.vendor_id = vm.vendor_id;
Если по какой-то причине, это по-прежнему является слишком медленным, единственной альтернативой, которая приходит на мой взгляд, это: Создать временную таблицу (своего рода «стратегии разделяй и властвуй»):
drop table if exists temp_vm;
create temporary table temp_vm
select distinct vendor_id
from vendor_master
where vendor_master_id = 12345;
alter table temp_vm
add index vi(vendor_id);
select i.*
from invoices as i inner join temp_vm as vm on i.vendor_id = vm.vendor_id;
Запомнить: временные таблицы видны только к соединению который создает их и удаляется при закрытии или завершении соединения.
В любом случае ваша производительность будет улучшена, если вы убедитесь, что ваши таблицы правильно проиндексированы; в частности, вам необходимо проиндексировать invoices.vendor_id
и vendor_master.vendor_master_id`.
Незначительное примечание. Вам не хватает ключевое слово 'WHERE' в подзапросе –