2

我有我要分析的这个 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

4

2 回答 2

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 匹配的任何 id Operator。也就是说,子查询生成两个表中所有id 的列表,因此NOT IN条件相当于外连接Operator和简单的测试 where o.ID IS NULL

这意味着CASE您的选择列表中的表达式是没有意义的,因为如果您的条件仅匹配Visitor不匹配Operator.

我认为您的查询中有一些严重混淆。

此外,您似乎在VisitorProfileFieldandProfileField表中使用了 EAV 反模式。这会给你带来很多麻烦。

于 2010-01-13T02:01:59.387 回答
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_”的连接表,您应该可以将其删除。如果您使用它只是为了确保该表中有访问者的记录,我会使用存在而不是连接。我放弃了那个。

我已将您的 not 切换为 not exists,我认为这可能更容易让 MySQL 优化。我使用了 IF 而不是 case,因为你只有两个,而且打字更短。我不知道任何一个在 MySQL 上是否更快/更容易。

我可以告诉你,根据我的经验,MySQL 性能随着 suqueries 中的子查询而死亡。它似乎放弃了优化它们并开始逐行运行它们。我敢打赌,如果您使用临时结果表(仅用于测试目的),您会发现查询运行得更快。

编辑:

比尔比我走得更远,我走得还不够远。我喜欢比尔的询问,并同意他关于 CASE 陈述的结论,这让我有点吃惊。

于 2010-01-13T01:51:26.950 回答