MySQL用子查询中的连接替换IN和EXISTS

时间:2014-12-11 13:02:12

标签: mysql join inner-join exists

因此,此查询目前在网上商店中用于检索有关文章的技术数据。 它的目的很好,除了最近显示的产品数量增加,导致某些类别的长时间加载时间不可接受。

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

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

因此,如果有人可以帮助我解决其中一个问题并解释如何替换in和存在加入,我可能会自己更改其他查询。

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

我认为这是次要子查询的主要问题

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

如果我遗漏了一些有用的信息,那么任何帮助或指示都会受到赞赏。

2 个答案:

答案 0 :(得分:1)

我认为这是等效的:

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子句中的比较移动到ON的{​​{1}}子句中。< / p>

可能有可能将整个事物弄平,而不是加入子查询。但我怀疑性能会很差,因为这不会使用JOIN来减少临时表。因此,您在产生的交叉产品中会发生组合爆炸,最后必须在顶部DISTINCT处减少。

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

答案 1 :(得分:0)

通常,您可以将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;评估成本很高。