如何在速度上改进这个mysql查询

时间:2010-07-02 21:26:08

标签: php mysql query-optimization

这里是

SELECT tbl_rls . * , (

SELECT count( * )
FROM `comments`
WHERE `post_id` = `tbl_rls`.`id`
) AS `t_comments`
FROM `tbl_rls`
WHERE 1 =1
AND `status` <> 'denied'
AND (
`id`
IN (

SELECT `rls_id`
FROM `tbl_visitors_logs`
WHERE `date` LIKE '2010-07-02%'
AND `page_type` = 'post'
GROUP BY `rls_id`
ORDER BY count( * ) DESC
)
)
AND (
`cat` = '6'
OR `cat`
IN (

SELECT `id`
FROM `tbl_cats`
WHERE `parent_id` = '6'
)
)
ORDER BY `tbl_rls`.`date` DESC
LIMIT 0 , 20

执行时几乎会杀死数据库,有人可以建议解决方案使其快速执行吗?

我在这里提供所需的任何其他信息。

感谢。

6 个答案:

答案 0 :(得分:2)

您是否运行EXPLAIN命令以查看查询的哪一部分运行缓慢?

此外,这一行可能是一个问题:WHERE date LIKE '2010-07-02%'这可能导致日期列被转换为字符串(请告诉我它不是字符串!),这将阻止使用任何索引。请改为WHERE DATE(date) = '2010-07-02'

答案 1 :(得分:1)

不要使用子查询。每个子查询对每行运行一次。因此,如果外部查询返回10行,则内部查询将运行10次。

由于子查询中有子查询,因此效果更差。效果倍增,所以如果外部的一行返回10行而内部的一行返回10行,那么最里面的一行将运行100次。

编辑:不要介意最后一段 - 看起来你在子查询中有一个子查询,但是再看一遍,你却没有。无论哪种方式,都不要使用子查询。

答案 2 :(得分:1)

你可以试试这个(没有数据很难测试)

SELECT r.*, COUNT(c.id) 
FROM tbl_rls r, comments c, tbl_visitors_logs v, tbl_cats t 
WHERE c.post_id = r.id 
    AND v.rls_id = r.id 
    AND t.parent_id = r.cat 
    AND r.status <> 'denied' 
    AND v.`date` LIKE '2010-07-02%' 
    AND page_type = 'post' 
    AND cat = 6 OR t.parent_id = 6 
GROUP BY c.post_id 
ORDER BY r.`date` DESC 
LIMIT 0, 20

这个数据结构是否正确?

CREATE TABLE IF NOT EXISTS `tbl_cats` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) NOT NULL,
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `tbl_rls` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `status` varchar(10) NOT NULL,
  `cat` int(11) NOT NULL,
  `date` date NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `tbl_visitors_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `rls_id` int(11) NOT NULL,
  `date` date NOT NULL,
  `page_type` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `post_id` int(11) NOT NULL,
  `commetn` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

答案 3 :(得分:1)

以下是我对您的查询的重写:

   SELECT t. *, 
          x.t_comments
     FROM tbl_rls t
LEFT JOIN (SELECT c.post_id,
                  COUNT(*) AS t_comments
             FROM COMMENTS c
         GROUP BY t.post_id) x ON x.post_id = t.id
     JOIN tbl_visitors_logs tvl ON tvl.rls_id = t.id
                               AND tvl.date LIKE '2010-07-02%'
                               AND tvl.page_type = 'post'
    WHERE t.status != 'denied'
      AND (t.cat = '6' OR t.cat IN (SELECT `id`
                                      FROM `tbl_cats`
                                     WHERE `parent_id` = '6'))
ORDER BY t.`date` DESC
   LIMIT 0, 20

答案 4 :(得分:1)

SELECT tbl_rls.*, 
       COUNT(distinct comments.id) AS t_comments 
  FROM tbl_rls 
       JOIN tbl_visitors_logs tvl ON tvl.rls_id = tbl_rls.id
        AND tvl.page_type =  'post'
        AND DATE(tvl.date) = '2010-07-02'
       LEFT JOIN comments ON comments.post_id = tbl_rls.id
       LEFT JOIN tbl_cats ON tbl_cats.id =  cat AND tbl_cats.parent_id = '6'
 WHERE status <> 'denied' 
   AND (cat = 6 OR tbl_cats.id is not null)
 GROUP BY tbl_rls.id                           
 ORDER BY tbl_rls.date DESC 
 LIMIT 0, 20 

答案 5 :(得分:1)

你能做的最好的事情是摆脱LIKE关键词,简单地说:

WHERE v.Date > '2010-07-02' AND v.Date < '2010-07-03'

这样,您将获得当天(或您需要的任何日期范围)的所有内容。考虑它的最好方法是mySQL必须经过并评估每一行,即使它们已经是一个datetime字段。如果定期搜索字段v.Date,你可以在其上加上一个索引以加快速度,然后它会使事情更快,因为它会知道数据已经存在。

您也可以使用COUNT(ID)代替计算所有内容。计算一个字段而不是10或20或50可以节省几毫秒。