【问题标题】:MySQL Replacing IN and EXISTS with joins in sub sub queriesMySQL用子子查询中的连接替换IN和EXISTS
【发布时间】:2015-02-09 23:29:09
【问题描述】:

因此,此查询目前在网上商店中用于检索有关文章的技术数据。 除了最近显示的产品数量增加导致某些类别的加载时间过长之外,它已经很好地发挥了作用。

对于最糟糕的页面之一,此(以及其他一些查询)被请求大约 80 次。

我最近才知道 MySQL 不会将没有依赖参数的子查询优化为只运行一次。

因此,如果有人可以帮助我解决其中一个查询并解释如何将 in 和 exists 替换为 join,我可能会自己更改其他查询。

select distinct criteria.cri_id, des_texts.tex_text, article_criteria.acr_value, article_criteria.acr_kv_des_id
from article_criteria, designations, des_texts, criteria, articles
where article_criteria.acr_cri_id = criteria.cri_id
and article_criteria.acr_art_id = articles.art_id
and articles.art_deliverystatus = 1
and criteria.cri_des_id = designations.des_id
and designations.des_lng_id = 9
and designations.des_tex_id = des_texts.tex_id
and criteria.cri_id = 328
and article_criteria.acr_art_id IN (Select distinct link_art.la_art_id
  from link_art, link_la_typ
  where link_art.la_id = link_la_typ.lat_la_id
  and link_la_typ.lat_typ_id = 17484
  and link_art.la_ga_id IN (Select distinct link_ga_str.lgs_ga_id
    from link_ga_str, search_tree
    where link_ga_str.lgs_str_id = search_tree.str_id
    and search_tree.str_type = 1
    and search_tree.str_id = 10132
    and EXISTS (Select *
      from link_la_typ
      where link_la_typ.lat_typ_id = 17484
      and link_ga_str.lgs_ga_id = link_la_typ.lat_ga_id)))
order by article_criteria.acr_value

我认为这是带有子子查询的主要坏人

我只是注意到我可以删除最后一个存在并且仍然得到相同的结果,但速度没有增加,虽然不是问题的一部分;)我会弄清楚我是否仍然需要那个部分。

感谢任何帮助或指点,如果我遗漏了一些有用的信息,请告诉我。

【问题讨论】:

  • 优化这种多表查询的第一步是重构它以更改连接样式。从 FROM tab1,tab2 ... WHERE ... tab1.col1 = tab2.col2 样式更改为 FROM tabl JOIN tab2 ON tab1.col1 = tab2.col2 样式。陌生人(比如 SO 上的我们)更容易以这种方式看到逻辑。
  • 另外,建议使用别名:from article_criteria -> from article_criteria ac,然后使用ac.acr_value 而不是article_criteria.acr_value。同一个思路——不必将表名放在列名中——cri_id 可以简单地为id。当有表前缀 criteria.cri_id 时,就像 criteria.id 一样可读。查询的大小可以减少 30% 甚至更多。
  • 谢谢 ^^ 我会记住的,这是我第一个使用 MySQL 的大型项目。学习清理代码 MySQL 在即将到来的最后期限内有些受苦;)并且我在原始查询中遗漏了表名,但为了将问题放在这里,我添加了它们。否则外人更难看到信息属于哪个表

标签: mysql join inner-join exists


【解决方案1】:

一般来说,您可以将FROM tab1 WHERE ... val IN (SELECT blah) 转换为这样的连接。

FROM tab1
JOIN (
        SELECT tab1_id
          FROM tab2
          JOIN tab3 ON whatever = whatever 
         WHERE whatever
      ) AS sub1 ON tab1.id = sub1.tab1_id

JOIN(内连接)将从查询中删除与 ON 条件不匹配的行。

如果您的 tab1_id 值可能与您的内部查询重复,请使用 SELECT DISTINCT。但除非需要,否则不要使用SELECT DISTINCT;评估成本很高。

【讨论】:

    【解决方案2】:

    我认为这是等价的:

    SELECT DISTINCT c.cri_id, dt.tex_text, ac.acr_value, ac.acr_kv_des_id
    FROM article_criteria AS ac
    JOIN criteria AS c ON ac.acr_cri_id = c.cri_id
    JOIN articles AS a ON ac.acr_art_id = a.art_id
    JOIN designations AS d ON c.cri_des_id = d.des_id
    JOIN des_texts AS dt ON dt.tex_id = d.des_tex_id
    JOIN (SELECT distinct la.la_art_id
          FROM link_art AS la
          JOIN link_la_typ AS llt ON la.la_id = llt.lat_la_id
          JOIN (SELECT DISTINCT lgs.lgs_ga_id
                FROM link_ga_str AS lgs
                JOIN search_tree AS st ON lgs.lgs_str_id = st.str_id
                JOIN link_la_typ AS llt ON lgs.lgs_ga_id = llt.lat_ga_id
                WHERE st.str_type = 1
                AND st.str_id = 10132
                AND llt.lat_typ_id = 17484) AS lgs
          ON la.la_ga_id = lgs.lgs_ga_id
          WHERE llt.lat_typ_id = 17484) AS la
    ON ac.acr_art_id = la.la_art_id
    WHERE a.art_deliverystatus = 1
    AND d.des_lng_id = 9
    AND c.cri_id = 328
    ORDER BY ac.acr_value
    

    所有IN <subquery> 子句都可以替换为JOIN <subquery>,然后您在被测试列上的JOIN 等于子查询返回的列。并且EXISTS 测试被转换为与表的连接,将子查询的WHERE 子句中的比较移动到JOINON 子句中。

    有可能将整个事物展平,而不是加入子查询。但我怀疑性能会很差,因为这不会减少使用DISTINCT 的临时表。所以你会在得到的叉积中得到组合爆炸,然后必须在最后用顶部的DISTINCT 减少它。

    我已将所有隐式连接转换为 ANSI JOIN 子句,以使结构更清晰,并添加表别名以使内容更具可读性。

    【讨论】:

    • 是的,这会返回与我原始查询完全相同的结果。真的有助于深入了解加入的工作原理,谢谢^^(还不能投票,当我可以的时候)遗憾的是我看不到速度的提高。我首先假设子查询被多次执行是错误的吗?还有其他方法可以提高此特定查询的速度吗?我想也许可以从子查询中得出一个观点。但 llt.lat_typ_id 和 st_str_id 会因页面而异(car_model/type 和 category_id 仅供参考)
    猜你喜欢
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 2013-02-12
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    相关资源
    最近更新 更多