【问题标题】:SQL Nested set query with join and sub query using "in" slow使用“in”慢速连接和子查询的 SQL 嵌套集查询
【发布时间】:2014-11-02 18:02:16
【问题描述】:

我在查询嵌套集结构时遇到了一些问题,速度很慢(约 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

【问题讨论】:

  • 根据你的更新更新了我的答案

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


【解决方案1】:

您的 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,这意味着您可以用inner join 替换所有left 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

【讨论】:

    【解决方案2】:

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

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

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

    (在所有上述情况中,我假设这些表已被合理索引?)

    【讨论】:

      【解决方案3】:

      你可以试试JOINing:

      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
      

      【讨论】:

        猜你喜欢
        • 2012-05-26
        • 2014-01-31
        • 2023-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        相关资源
        最近更新 更多