使用多个LEFT JOIN的SQL查询如何提高性能?

3

我有一个数据库结构,用于提供新闻文章,以下是相关联的内容:

  • 多对多关系 news_categories
  • 多对多关系 tags
  • 多对多关系 uploads

我编写了一条SQL查询语句来汇总所有内容:

 SELECT `news_articles`.*, 
 GROUP_CONCAT(DISTINCT tags.title) AS `tags`, 
 GROUP_CONCAT(DISTINCT tags.id) AS `tag_ids`,
 GROUP_CONCAT(DISTINCT news_categories.title) AS `news_categories`,
 GROUP_CONCAT(DISTINCT news_categories.id) AS `news_category_ids`,
 GROUP_CONCAT(DISTINCT news_categories.slug) AS `news_category_slugs`, 
 `news_articles_uploads`.`caption` AS `upload_caption`,
 `uploads`.`title` AS `upload_title`, 
 `uploads`.`basename` AS `upload_basename`,
 `uploads`.`extension` AS `upload_extension`,
 `uploads`.`path` AS `upload_path`
 FROM `news_articles`
 LEFT JOIN `news_articles_tags` ON news_articles_tags.news_article_id = news_articles.id
 LEFT JOIN `tags` ON news_articles_tags.tag_id = tags.id
 LEFT JOIN `news_articles_news_categories` ON news_articles_news_categories.news_article_id = news_articles.id
 LEFT JOIN `news_categories` ON news_articles_news_categories.news_category_id = news_categories.id
 LEFT JOIN `news_articles_uploads` ON (news_articles_uploads.news_article_id = news_articles.id AND news_articles_uploads.order = 0)
 LEFT JOIN `uploads` ON news_articles_uploads.upload_id = uploads.id 
 WHERE (news_categories.slug IN ("category-one","category-two","category-three","category-four","category-five")) AND (news_articles.published = 1)
 GROUP BY `news_articles`.`id`
 ORDER BY `news_articles`.`lead_article` DESC, `news_articles`.`created` DESC LIMIT 20;

问题在于查询运行速度缓慢,在繁忙时期,CPU使用率变得相当失控!以下是上述查询的解释(右键单击在新选项卡中查看完整大小):您可以在此处找到模式:http://pastie.org/private/qoe2qo16rbqr5mptb4bug服务器运行MySQL 5.1.55,网站使用Zend Framework执行查询和PHP 5.2.8。我已经检查了MySQL慢查询日志,并尽我所知添加了缺少的索引,但是该查询仍然需要1-3秒钟才能执行。如果有人有任何想法,我将非常感激。提前致谢。
2个回答

3

请问您在news_articles.lead_articlenews_articles.created这两个字段上是否有索引,或者说是否有同时包含这两个字段的索引?

create index news_articles_x1 on news_articles (lead_articles, created);

如果没有它,您将无法利用order by limit子句,它仍将扫描并对整个表进行排序。

此外,我想问一下您是否需要一次获取所有这些数据?


+1 对于“你是否需要一次性获取所有这些数据”,但是你在索引中漏掉了news_articles.id - Ben
谢谢回复,我创建了索引,但是EXPLAIN结果仍然相同。我应该看到任何不同的东西吗? - BeesonBison
所有数据一次性传输的问题...我可能可以去掉标签,但绝对需要类别和上传连接。 - BeesonBison
谢谢Ben,我已经在news_articles.id上添加了一个索引。 - BeesonBison

3

由于您的“WHERE”子句最初包含了指定列表中的新闻类别的“AND”,这将强制执行INNER连接而不是LEFT JOINs。此外,我建议尝试添加“STRAIGHT_JOIN”子句。这通常会强制引擎按照特定顺序进行连接,而不是尝试为您提供自己的替代方案...特别是当其他表是更多的“查找”引用时。

我还建议按照Jordan的建议应用索引。

SELECT STRAIGHT_JOIN
      NA.*, 
      GROUP_CONCAT(DISTINCT tags.title) AS `tags`, 
      GROUP_CONCAT(DISTINCT tags.id) AS tag_ids,
      GROUP_CONCAT(DISTINCT NC.title) AS news_categories,
      GROUP_CONCAT(DISTINCT NC.id) AS news_category_ids,
      GROUP_CONCAT(DISTINCT NC.slug) AS news_category_slugs, 
      NAUp.`caption` AS upload_caption,
      Up1.`title` AS upload_title, 
      Up1.`basename` AS upload_basename,
      Up1.`extension` AS upload_extension,
      Up1.`path` AS upload_path
   FROM 
      news_articles NA
         INNER JOIN news_articles_news_categories NACats
            ON NA.id = NACats.news_article_id

            INNER JOIN news_categories NC
               ON NACats.news_category_id = NC.id
               AND NC.slug IN ( "category-one",
                                "category-two",
                                "category-three",
                                "category-four",
                                "category-five" )


         LEFT JOIN news_articles_tags NATags
            ON NA.ID = NATags.news_article_id

            LEFT JOIN tags
               ON NATags.tag_id = tags.id

         LEFT JOIN news_articles_uploads NAUp
            ON    NA.ID = NAUp.news_article_id 
              AND NAUp.order = 0

            LEFT JOIN uploads Up1
               ON NAUp.upload_id = Up1.id 

   WHERE 
      NA.Published = 1
   GROUP BY 
      NA.ID
   ORDER BY 
      NA.lead_article DESC, 
      NA.created DESC 
   LIMIT 20;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接