SQL嵌套集查询,使用“in”缓慢加入和子查询

时间:2014-09-09 10:39:12

标签: mysql sql nested-sets nested-set-model

我对嵌套集结构的查询有些麻烦(~4秒)

select node.ID, node.Lft, node.Rgt, node.Level, count(ag.ArticleID) as count
from Webshop.Category node
left join Webshop.Category parent on parent.Lft between node.Lft and node.Rgt
left join Webshop.ArticleGroup ag on parent.GroupID = ag.GroupID
    and ag.ArticleID in (
        select a.ID
        from Webshop.Article a
        join Webshop.ArticleOwner ao on ao.ArticleID = a.ID and ao.OWNRID = 1
        join Webshop.ArticleAssortment aa on aa.ArticleID = a.ID and aa.AssortmentID = 6
    )
group by node.ID
having count > 0

该解释返回以下内容:

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,node,ALL,NULL,NULL,NULL,NULL,2538,"Using temporary; Using filesort"
1,PRIMARY,parent,ALL,Lft,NULL,NULL,NULL,2538,
1,PRIMARY,ag,ref,fk_ArticleGroup_Group1_idx,fk_ArticleGroup_Group1_idx,4,Webshop.parent.GroupID,9,"Using index"
2,"DEPENDENT SUBQUERY",a,eq_ref,PRIMARY,PRIMARY,4,func,1,"Using index"
2,"DEPENDENT     SUBQUERY",ao,eq_ref,"ArticleIDOWNRID,fk_ArticleOwner_Article1_idx,fk_ArticleOwner_OWNR1_idx",ArticleI    DOWNRID,8,"Webshop.a.ID,const",1,"Using index"
2,"DEPENDENT SUBQUERY",aa,eq_ref,"PRIMARY,fk_ArticleAssortment_Article1_idx",PRIMARY,8,"Webshop.a.ID,const",1,"Using index"

我认为使用in()的子查询会使查询变慢。有更好的方法吗?

感谢。

编辑:

我忘了从子查询中删除联接中的left

3 个答案:

答案 0 :(得分:0)

您可以尝试JOIN

select node.ID, node.Lft, node.Rgt, node.Level, count(ag.ArticleID) as count
from Webshop.Category node
left join Webshop.Category parent on parent.Lft between node.Lft and node.Rgt
left join Webshop.ArticleGroup ag on parent.GroupID = ag.GroupID
        join (
        select a.ID as `id`
        from Webshop.Article a
        left join Webshop.ArticleOwner ao on ao.ArticleID = a.ID and ao.OWNRID = 1
        left join Webshop.ArticleAssortment aa on aa.ArticleID = a.ID and aa.AssortmentID = 6
    ) in_ids on ag.ArticleID = in_ids.id
group by node.ID
having count > 0

答案 1 :(得分:0)

首先,你有一个左连接加载,左连接很慢 - 我假设你正在使用它们,因为它们在这里是必要的(即连接的表可能不包含匹配,你想要返回每一行Webshop.Category,无论是否存在连接的行)。 如果没有必要左连接,我会转换为内连接。 如果左连接 是必要的,我会考虑更好的数据库设计,以消除对这些左连接的需要。这可能是不可避免的,但我会感到惊讶。

如果无法转换为内部联接,或者查询仍然很慢,那么您可以尝试首先将子查询作为insert into语句运行,创建临时表,然后使用该临时表代替sub查询(可能是连接而不是“in”)。

如果仍然很慢,您可以尝试在ID上索引该临时表。

(在上述所有情况中,我假设这些表是明智的索引?)

答案 2 :(得分:0)

您的IN子查询包含不会影响结果的LEFT JOIN子句,因此首先您可以将其更改为:

select node.ID, node.Lft, node.Rgt, node.Level, count(ag.ArticleID) as count
from Webshop.Category node
left join Webshop.Category parent on parent.Lft between node.Lft and node.Rgt
left join Webshop.ArticleGroup ag on parent.GroupID = ag.GroupID
    and ag.ArticleID in (
        select a.ID
        from Webshop.Article a
    )
group by node.ID
having count > 0

现在,一旦我们完成了这项工作,我们就可以看到您正在检查AritcleID外键的一致性。所以有两种选择。首先,有一个外键,你不必担心这一点,完全删除IN

select node.ID, node.Lft, node.Rgt, node.Level, count(ag.ArticleID) as count
from Webshop.Category node
left join Webshop.Category parent on parent.Lft between node.Lft and node.Rgt
left join Webshop.ArticleGroup ag on parent.GroupID = ag.GroupID
group by node.ID
having count > 0

第二个选项,你没有外键。创建一个并不是一个坏主意。

<强>更新 我刚刚注意到您在查询结尾处有count > 0,这意味着您可以将所有left join子句替换为inner join,结果相同。

当您在子查询中使用left joins替换inner join时,唯一可能改变速度的方法是用IN替换EXISTS

select node.ID, node.Lft, node.Rgt, node.Level, count(ag.ArticleID) as count
from Webshop.Category node
inner join Webshop.Category parent on parent.Lft between node.Lft and node.Rgt
inner join Webshop.ArticleGroup ag on parent.GroupID = ag.GroupID
WHERE EXISTS (
    SELECT * FROM
        from Webshop.Article a
        join Webshop.ArticleOwner ao on ao.ArticleID = a.ID and ao.OWNRID = 1
        join Webshop.ArticleAssortment aa on aa.ArticleID = a.ID and aa.AssortmentID = 6 
    WHERE a.ID = ag.ArticleID
    )
group by node.ID
-- you don't need HAVING clause here