使用MySql 5.6,我注意到合并Select
个查询(例如select x.a from X x where x.b in (select y.b from Y y where y.c = 'something')
)比使用in
中第一个查询的结果慢两个单独的查询第二个查询的子句。我尝试使用Join
语句而不是嵌套查询(受此站点上其他帖子的影响)不会产生任何性能改进。
我知道这是MySql的一个常见问题,我在这里看过很多关于这个问题的帖子,并尝试了一些显然适用于其他海报的解决方案,但不适用于我。
此查询:
select ADSH_ from SECSub where Symbol_='MSFT';
很快并产生了这个结果:
'0001193125-10-015598'
'0001193125-10-090116'
'0001193125-10-171791'
实际上有21个结果,但我已将这个结果修剪为3个。
以下是其他一些信息:
show indexes from SECSub;
产生
和
explain select * from SECSub where Symbol_='MSFT';
产生
使用第一个查询的结果查询不同的表,如下所示:
select * from SECNum where ADSH_ in (
'0001193125-10-015598',
'0001193125-10-090116',
'0001193125-10-171791);
同样快(.094秒)。实际查询的in
子句使用了第一个查询的21个结果,但我再次将它们修剪为3个。
而且:
show indexes from SECNum;
产生
和
explain select * from SECNum where ADSH_ in (
'0001193125-10-015598',
'0001193125-10-090116',
'0001193125-10-171791');
产生
但是合并查询:
select *
from SECNum
where ADSH_ in (select ADSH_
from SECSub sub
where Symbol_='MSFT');
非常慢,耗时151秒(相比之前的查询大约0.1秒)。
explain select * from SECNum where ADSH_ in (select ADSH_ from SECSub sub where Symbol_='MSFT');
产生
所以,在阅读了一些关于SO的类似帖子之后,我尝试将组合查询重新强制转换为Join
操作:
select *
from SECNum num
inner join SECSub sub on num.ADSH_ = sub.ADSH_
where sub.Symbol_ = 'MSFT';
这个耗时158秒的结果甚至比使用合并查询要慢一些,耗时151秒。
explain select * from SECNum num inner join SECSub sub on num.ADSH_ = sub.ADSH_ where sub.Symbol_ = 'MSFT';
制备:
select *
from (select sub.ADSH_
from SECSub sub
where sub.Symbol_='MSFT') SubSelect
join SECNum num on SubSelect.ADSH_ = num.ADSH_;
此结果的时间为151秒,与我的组合查询相同..
explain select * from (select sub.ADSH_ from SECSub sub where sub.Symbol_='MSFT') SubSelect join SECNum num on SubSelect.ADSH_ = num.ADSH_;
制备:
很明显,我不知道我在做什么(还)。有关如何编写与我的组合查询或任何这些Join查询产生相同结果的查询的任何建议,其运行速度与我有两个单独查询(大约0.1秒)的情况一样快?
答案 0 :(得分:1)
让我先从这个问题开始:
select *
from SECNum
where ADSH_ in (select ADSH_
from SECSub sub
where Symbol_ = 'MSFT');
对此的最佳索引是复合索引SECSub(Symbol_, ADSH_)
。我猜是因为这个索引不可用,MySQL似乎做出了错误的选择。它正在执行全表扫描并检查where
条件,而不是使用索引来查找适当的行。覆盖索引(带有两列)应该将MySQL优化器放在正确的路径上。
有时候,带有子查询的in
没有得到很好的优化(虽然我认为这在5.6中得到了修复)。还可以使用not exists
:
select *
from SECNum sn
where not exists (select ADSH_
from SECSub sub
where sub.Symbol_ = 'MSFT' AND
sub.ADSH_ = sn.ADSH_
);
答案 1 :(得分:1)
IN ( SELECT ... )
效果不佳。实际上,直到5.6它才能很好地优化非常。 5.6增加了一种有用的技术。但通常最好将其转换为JOIN,即使是5.6。
FROM ( SELECT ... ) a
JOIN ( SELECT ... ) b ON ...
在5.6之前,执行非常的原因很差,因为子查询都没有索引,因此很多表扫描了一个tmp表。 5.6(或它是5.7?)'发现'子查询的最佳索引,从而有助于显着。
FROM tbl
JOIN ( SELECT ... ) x ON ...
将始终(至少在5.6之前)首先执行子查询,进入临时表。然后它将执行NLJ(嵌套循环连接)。因此,对于ON
子句中的任何列,您有理由在tbl中有一个索引。如果有多列,请将其作为复合索引。
复合查询通常比单列查询更好。请记住,MySQL几乎从不在单个SELECT中使用两个索引。 (“索引合并”)
每当提出效果问题时,请提供SHOW CREATE TABLE
。
根据这些原则,您应该能够编写性能更好的查询,而无需进行如此多的实验。
答案 2 :(得分:0)
首先,我尝试了@Gordon Linoff的建议(或暗示的建议)在SECSub上添加由Symbol_和ADSH_组成的复合索引。这对我尝试的任何查询的性能没有任何影响。
在遇到此性能问题时,我注意到SECNum.ADSC_
被定义为character set latin1
而SECSub.ADSC_
被定义为character set utf8_general_ci
。
然后我怀疑当我通过复制创建第二个查询并粘贴第一个查询的输出时:
select * from SECNum where ADSH_ in (
'0001193125-10-015598',
'0001193125-10-090116',
'0001193125-10-171791');
in
子句中的文字字符串使用character set latin1
,因为它们都是在MySQL Workbench中输入的(好,复制和粘贴),这可能解释了为什么这个查询如此快速
这样做之后:
alter table SECSub convert to character set latin1;
组合查询(子查询)很快(不到1秒),并且explain
第一次显示查询正在使用索引。使用Join
的变体也是如此。
我想如果我在原始问题中包含实际的表定义,有人会告诉我,分配给参与索引和查询的表列的字符集存在不一致。学过的知识。下次发布时,我将包含表定义(至少对于参与索引和查询的那些列而言,我会问。)