【问题标题】:How to improve this mysql query in speed如何提高这个mysql查询的速度
【发布时间】:2010-07-02 21:26:08
【问题描述】:

这里是

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

这几乎在执行时杀死了数据库,有人可以提出解决方案以使其更快吗?

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

谢谢。

【问题讨论】:

  • 它应该做什么。把你的mysql表粘贴到这里,仅仅从查询中很难解决问题

标签: php mysql query-optimization


【解决方案1】:

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

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

【讨论】:

  • 它不是字符串,它是 Datetime 字段。但我想要单个日期记录和值就像'2010-07-02 12:03:49'。
  • 这正是它的用途:截断 datetime 的时间:mysql> SELECT DATE('2003-12-31 01:02:03'); -> '2003-12-31'
【解决方案2】:

这是我对您的查询的重写:

   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

【讨论】:

    【解决方案3】:

    不要使用子查询。每个子查询对每一行运行一次。所以如果外部查询返回 10 行,那么内部查询将运行 10 次。

    由于您在子查询中有一个子查询,因此效果会变得更糟。效果成倍增加,所以如果外层返回 10 行,内层返回 10 行,那么最内层将运行 100 次。

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

    【讨论】:

    • 您实际上是在谈论相关的子查询,如果子查询如此糟糕——它们就会被删除。
    • 并非如此。相关的子查询太糟糕了,它们还没有被删除。
    【解决方案4】:

    你能做的最好的事情就是去掉 LIKE 关键字,然后简单地说:

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

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

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

    【讨论】:

      【解决方案5】:
      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 
      

      【讨论】:

        【解决方案6】:

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

        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 ;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-10-09
          • 2021-11-17
          • 2021-12-24
          • 2012-03-31
          • 2012-01-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多