我在使用IN语句高效运行SQL时遇到了一些麻烦。如果我单独运行这两个语句,并手动粘贴一系列结果(在这种情况下有30个vendor_id),vendor_master查询立即运行,发票查询在大约2秒内运行。
select * FROM invoices where vendor_id IN
(
select vendor_id from vendor_master WHERE vendor_master_id = 12345
);
那么是什么导致了巨大的减速,超过60秒并经常超时?有没有办法用逗号将结果放在变量中?或者让内部语句执行第一个?
答案 0 :(得分:3)
在MySQL 5.6.6之前,in
的优化效率非常低。请改用exists
:
select *
FROM invoices i
where exists (select 1
from vendor_master vm
where i.vendor_id = vm.vendor_id and vm.vendor_master_id = 12345
);
为获得最佳效果,您需要vendor_master(vendor_id, vendor_master_id)
上的索引。
答案 1 :(得分:1)
您可以尝试使用INNER JOIN
:
select i.*
FROM invoices i
INNER JOIN vendor_master vm
ON i.vendor_id = vm.vendor_id AND vm.vendor_master_id = 12345
答案 2 :(得分:1)
您可以JOIN
使用DISTINCT
代替IN
:
SELECT *
FROM invoices JOIN
(
SELECT DISTINCT vendor_id as vid
FROM vendor_master
WHERE vendor_master_id = 12345
) vmi
ON invoices.vendor_in = vmi.vid
请记住,您必须拥有DISTINCT
,否则如果内部查询有两条记录,那么您将在JOIN
之后重复行,结果将不同于{{ 1}}查询。
答案 3 :(得分:1)
那么是什么导致巨大的减速,超过60秒而且经常超时?
当IN
条件内的数据集“小”和“确定”时,IN
子句运行良好。那是因为每行一次评估条件。因此,假设IN
子句中的查询返回100行而FROM
子句中的表有1000行,则服务器必须执行100 * 1000 = 100,000
比较以过滤掉您的数据。过滤太少的数据太费力了,你不觉得吗?当然,如果您的数据集(from
和in
条款中的数据集都较大),您可以想象效果。
顺便说一下,当您使用子查询作为in
条件时,还会产生额外的开销:子查询需要为每一行执行一次。所以序列是这样的:
要做太多工作,你不觉得吗?
有没有办法用逗号将结果放在变量中?
是的,有办法...但你真的想要这样做吗?我们来看看:
首先,创建一个包含您要过滤的值的列表:
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`编入索引。