我正在尝试分析这个mysql查询。它非常慢,这里的访问者表是大约50K条目,这个查询永远不会返回。当我尝试解释语句时,我发现索引没有在访问者表上使用,尽管索引可用。现在这是我需要帮助解决的难题。任何提示赞赏。
查询:
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
答案 0 :(得分:2)
根据我推断的查询,以下内容应该产生相同的结果,没有子查询和更快的性能。
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
中任何ID匹配的任何Operator
个ID。也就是说,子查询生成两个表中的所有 id的列表,因此NOT IN
条件等同于Operator
的外连接和o.ID IS NULL
的简单测试{1}}。
这意味着您的选择列表中的CASE
表达式毫无意义,因为如果您的条件仅匹配Visitor
与Operator
中的任何行不匹配的行,它肯定会为0
我认为你的查询中存在一些问题。
此外,您似乎在VisitorProfileField
和ProfileField
表格中使用了EAV反模式。这会给你带来很多麻烦。
答案 1 :(得分:1)
你的查询是......大。你能解释一下它对你的影响吗?看起来它会拉出每个访客ID,以及他们是否是运营商,他们不是运营商,他们有特定的配置文件设置。这没有多大意义,所以我必须在那里遗漏一些东西。
这是我的尝试,基于我对你要做的事情的理解:
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。我放弃了。
我已经切换到了不存在,我认为MySQL可能更容易优化。我使用的是IF而不是一个案例,因为你只有两个,而且输入的时间更短。我不知道在MySQL上是否更快/更容易。
我可以告诉你,根据我的经验,MySQL性能会随着suqueries中的子查询而消失。它似乎放弃了优化它们并开始逐行运行它们。我敢打赌,如果您使用临时结果表(仅用于测试目的),您会发现查询运行得更快。
修改强>
比尔比我做得更远,我做得还不够。我喜欢比尔的询问,并同意他关于CASE陈述的结论,这有点让我失望。