提高MySQL查询性能

时间:2014-01-22 23:22:46

标签: mysql

我有以下查询连接5个InnoDB相关表以获得10行的所需结果集,我尽力通过添加索引并以多种不同方式重新编写查询来解决问题,但我最终要么意外的结果或非常缓慢的查询。

这里是QUERY

SELECT 
    a.*,
    c.id as category_id,
    c.title as catname,
    CONCAT(u.fname, ' ', u.lname) as username,
    DATE_FORMAT(a.created, '%W %M %d, %Y - %T') as long_date,
    DATE_FORMAT(a.created, '%d/%m/%Y - %T') as short_date,
    (SELECT 
            COUNT(article_id)
        FROM
            comment
        WHERE 
            article_id = a.id) as totalcomments,
    YEAR(a.created) as year,
    MONTH(a.created) as month,
    DAY(a.created) as day
FROM
    article as a
        INNER JOIN
    article_related_categories rc ON a.id = rc.article_id
        LEFT JOIN
    category as c ON c.id = rc.category_id
        LEFT JOIN
    user as u ON u.id = a.user_id
WHERE
    rc.category_id = 1
        AND a.created <= NOW()
        AND (a.expire = '0000-00-00 00:00:00'
        OR a.expire >= NOW())
        AND a.published IS NOT NULL

ORDER BY a.created DESC
LIMIT 0 , 10

Click Here to see the explain screenshot

目前文章表中有超过13,000行,预计会有快速增长。

问题是,此查询可能需要大量时间才能执行,大约需要3-4秒。我怀疑INNER JION导致了大部分问题,但我想我会在这里询问是否有人提出改进此查询性能的任何想法。

3 个答案:

答案 0 :(得分:0)

嵌套SELECT可能会减慢速度。加入comment表和GROUP BY a.id

...
    COUNT(*) as totalcomments,
...    
FROM
    ...
    LEFT JOIN comment AS comm.article_id = a.id
WHERE
    ...
GROUP BY a.id

答案 1 :(得分:0)

快速修复就是摆脱这个

    AND a.created <= NOW()

因为将来创建的文章确实没有意义。数据库通常(几乎总是)做的事情少一点就会导致执行速度加快。

回答的难点是不知道你真正希望从数据库获得什么。您需要考虑左连接并在适用的情况下消除它们。问题是你没有删除左连接的行和较小的结果集,就像你通过消除行返回的那些更简单,因为结果集更小。

为了获得最佳速度,我将从相关类别表开始,因为我已经将where语句中的结果缩小到1,而我只查看related_category的一个不同值。

select blah from related_categories rc 
join comment c on r.id = c.id 
join blah b on b.id = c.id
where rc.id = 1

答案 2 :(得分:0)

我会在你的桌子上有一个索引

article table index -- ( published, expire, id )
article table index -- ( id ) just the primary key ID for secondary join criteria
article_related_categories table index( article_id, category_id )
comment table (article_id)

然后,让预先查询除了获取ID和文章并对相关内容进行计数之外什么也不做 感兴趣的类别,订单和限制10篇文章......然后加入类别和用户 表格为你的最终输出。

SELECT
      a2.*,
      c.id as category_id,
      c.title as catname,
      CONCAT(u.fname, ' ', u.lname) as username,
      DATE_FORMAT(a2.created, '%W %M %d, %Y - %T') as long_date,
      DATE_FORMAT(a2.created, '%d/%m/%Y - %T') as short_date,
      PreQual.TotalComments,
      YEAR(a2.created) as year,
      MONTH(a2.created) as month,
      DAY(a2.created) as day
   from 
      ( select 
              a.id,
              rc.category_id,
              COUNT(c.article_id) as TotalComments
           from 
              article a
                 join article_related_categories rc 
                    ON a.id = rc.article_id
                    AND rc.category_id = 1
                 left join comment c
                    ON a.id = c.article_id
           where
                  a.published IS NOT NULL
              AND (    a.expire >= now()
                    OR a.expire = '0000-00-00 00:00:00' )
           group by
              a.id,
              rc.category_id
           order by
              a.created DESC
           limit
              0, 10 ) PreQual
        JOIN article a2
           ON PreQual.ID = a2.id
           LEFT JOIN user u
              ON a2.user_id = u.id
        LEFT JOIN category as c 
           ON PreQual.Category_ID = c.id

现在,即使使用上述查询,执行基于Web的活动(它出现),并在相关条件下从整个子集进行计数也可能是一个巨大的性能损失。您最好在一定程度上对数据进行DE标准化。在文章表中,为CommentCount添加一列。然后,当添加任何新注释时,在插入基本上执行

的注释后触发
update Articles
   set CommentCount = CommentCount +1
   where id = the article ID of the new comment ID just inserted.

然后,你永远不必每次都回去做COUNT()。这将是您最好的操作行动。在创建触发器之前,您必须默认所有计数,但这将是计数的一次性相关更新。您只需要返回相关的文章类别表,以符合您感兴趣的类别标准。