我有一个我正在尝试分析的mysql查询。它非常慢,这里的访问者表大约有50K个条目,这个查询永远不会返回。当我尝试使用explain语句时,我发现尽管索引可用,但未在访问者表上使用该索引。这就是我需要帮助解决的大难题。任何提示都很感谢。
查询:
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
发布于 2010-01-13 10:01:59
根据我对您的查询的推断,下面的语句应该会产生相同的结果,没有子查询,并且性能更快。
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谓词似乎排除了与Operator中的任何id匹配的任何Visitor id。也就是说,子查询生成两个表中所有id的列表,因此NOT IN条件相当于到Operator的外连接和一个简单的测试where o.ID IS NULL。
这意味着选择列表中的CASE表达式是没有意义的,因为如果您的条件只匹配与Operator中的任何行都不匹配的Visitor行,那么它肯定是0。
我认为您的查询中有一些严重的混淆。
而且,您似乎在VisitorProfileField和ProfileField表中使用了EAV反模式。这会给你带来很多麻烦。
发布于 2010-01-13 09:51:27
您的查询是...大的。你能解释一下它对你有什么好处吗?它看起来像是拉取每个访问者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。我把它弄丢了。
我已经把你的not换成了not exists,我觉得这对MySQL来说可能更容易优化。我用IF代替大小写,因为你只有两个大小写,而且输入起来更短。我不知道哪一个在MySQL上更快/更容易。
我可以告诉你,根据我的经验,MySQL的性能会随子查询中的子查询而消亡。它似乎放弃了对它们的优化,开始逐行运行它们。我敢打赌,如果您使用临时结果表(仅用于测试目的),您会发现查询运行得更快。
编辑:
比尔比我走得更远,我走得还不够远。我喜欢比尔的质疑,并同意他关于案件陈述的结论,这有点让我吃惊。
https://stackoverflow.com/questions/2053757
复制相似问题