【问题标题】:MySQL optimization query with subqueries带有子查询的 MySQL 优化查询
【发布时间】:2013-01-04 17:04:17
【问题描述】:

今天我收到了来自我的托管帐户的电子邮件,说我需要调整我的查询:

SELECT
  `id`, `nick`, `msg`, `uid`, `show_pic`,
  `time`,`ip`,`time_updated`,
  (SELECT COUNT(c.msg_id)
   FROM `the_ans` c
   where c.msg_id = d.id) AS counter,
  (SELECT c.msg
   FROM `the_ans` c
   WHERE c.msg_id=d.id
   ORDER BY `time` DESC LIMIT 1) as lastmsg
FROM
  `the_data` d
ORDER BY `time_updated` DESC LIMIT 26340 ,15

解释:

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY d ALL 34309 Using filesort
3 DEPENDENT SUBQUERY c ALL 43659 Using where; Using filesort
2 DEPENDENT SUBQUERY c ALL 43659 Using where

此查询检查 65,396,669,012,829 行,这在共享主机中是不可接受的。

tbh,我不明白他们的解释.. 查询的实际作用是按时间更新 15 个帖子, 对于每个帖子,我都会获取最新评论, 计算每个帖子的所有 cmets。

posts table - 'the_data'

comments table = 'the_ans'

我不是 mysql 专家,我不知道如何改进这个查询 任何帮助将不胜感激

谢谢

查询

SELECT
  `id` , `nick` , `msg` , `uid` , `show_pic` , `time` , `ip` , `time_updated` , (
    SELECT COUNT( c.msg_id )
    FROM `the_ans` c
    WHERE c.msg_id = d.id
   ) AS counter, (
    SELECT c.msg
    FROM `the_ans` c
    WHERE c.msg_id = d.id
    ORDER BY `time` DESC
    LIMIT 1
   ) AS lastmsg
FROM `the_data` d
ORDER BY `time_updated` DESC
LIMIT 26340 , 15 

这是结果结构

id| nick  | msg  | uid   | show_pick | time      | ip |time_updated|counter|lastmsg
  |       |      |       |           |           |    |            |       |
7 | jqman | hello| 10074 |   0       |2013-21-01 | 12 |2013-21-01  | 55    |blah bl

【问题讨论】:

  • 您确定LIMIT 26340, 15 部分吗?
  • 基本问题是需要执行查询来获取你的15行。要执行主查询,需要为处理的每一行执行每个子查询。您可以为每个表发布 show create table 的结果吗?您是否为每个表中的“id”创建了索引?
  • 一般来说,一个不相关的子查询会比一个相关的子查询执行得更好。通用语法如下: SELECT x.* FROM my_table x JOIN (SELECT id,MAX(time) max_time FROM my_table GROUP BY id) y ON y.id = x.id AND y.max_time = x.time;这将为您提供每个线程中的最新消息。然后你只需将其他信息加入其中(反之亦然)
  • 不,我没有添加索引,我每个表只有一个索引(id)
  • @inhan - 这是分页机制的一部分,我们可以限制为 15,我已将结构添加到主帖子

标签: mysql query-optimization subquery


【解决方案1】:

快速浏览一下解释计划表明没有合适的索引供 MySQL 使用,所以它求助于全表扫描。

 EXPLAIN: 
 id select_type        table type possible_keys key key_len ref rows  Extra 
 -- ------------------ ----- ---- ------------- --- ------- --- ----- ---------------------------- 
 1  PRIMARY            d     ALL                                34309 Using filesort
 3  DEPENDENT SUBQUERY c     ALL                                43659 Using where; Using filesort 
 2  DEPENDENT SUBQUERY c     ALL                                43659 Using where

要优化现有查询的执行,您需要添加适当的索引。可能的候选人:

ON `the_data`(`time_updated`)
ON `the_ans`(`msg_id`,`time`)

这些索引将显着提高外部查询(可能消除排序操作)和相关子查询的大量执行的性能。


除此之外,您还需要更改查询以提高性能。最外层查询的LIMIT 子句在整个结果集准备好后应用,这意味着这两个相关的子查询将针对表the_data 中的每一行执行。就性能而言,这会吃掉你的午餐。

要让这些相关子查询仅针对返回的(最多)15 行运行,您需要在这些子查询运行之前应用 LIMIT 子句。

这个查询应该返回一个等效的结果集,并且将避免每个相关子查询的 34,000 多次执行,这应该会大大提高性能:

SELECT d.*
     , ( SELECT COUNT( c.msg_id )
           FROM `the_ans` c
          WHERE c.msg_id = d.id
       ) AS counter
     , ( SELECT c.msg
           FROM `the_ans` c
          WHERE c.msg_id = d.id
          ORDER BY `time` DESC
          LIMIT 1
       ) AS lastmsg
  FROM ( SELECT e.`id` 
              , e.`nick`
              , e.`msg`
              , e.`uid`
              , e.`show_pic`
              , e.`time`
              , e.`ip`
              , e.`time_updated` 
           FROM `the_data` e
          ORDER
             BY e.`time_updated` DESC
          LIMIT 26340 , 15 
       ) d
 ORDER BY d.`time_updated` DESC

(您当前的查询执行每个相关子查询“SELECT COUNT(1) FROM the_data”次。使用上面重写的查询,每个子查询将仅执行 15 次。)

【讨论】:

  • 好吧,看来最大的问题是我没有使用索引。同样,当使用你的方法时,查询的执行速度提高了大约 16%。谢谢!
  • 每天添加索引的速度提高了 1000%,我们学到了一些新东西
  • @JQman:是的,没有索引是个大问题。这是一个无需部署任何代码更改即可应用的修复程序。添加这些索引的查询的 EXPLAIN 输出会很好。
【解决方案2】:

这样的事情应该会给你更快的结果。

请注意,这是使用 INNER JOIN,因为它旨在当 *the_data* 上的每条记录在 *the_ans* 上都有至少一个匹配记录时工作

SELECT `id` , `nick` , `msg` , `uid` , `show_pic` , `time` , `ip` , `time_updated` , Sub1.counter, c.msg AS lastmsg
FROM `the_data` d
INNER JOIN (SELECT msg_id, COUNT( * ) AS counter, MAX( `time` ) AS MaxTime FROM `the_ans` GROUP BY msg_id) Sub1 ON d.id = Sub1.msg_id
INNER JOIN the_ans c ON d.id = c.msg_id AND sub1.MaxTime = c.`time`
ORDER BY `time_updated` DESC
LIMIT 26340 , 15 

【讨论】:

    【解决方案3】:

    从主查询中选择有时间限制的行后执行相关子查询:

    SELECT d.*,
           (SELECT COUNT(c.msg_id)
            FROM `the_ans` c
            where c.msg_id = d.id) AS counter,
           (SELECT c.msg
            FROM `the_ans` c
            WHERE c.msg_id=d.id
            ORDER BY `time` DESC LIMIT 1) as lastmsg
    FROM (SELECT
            `id`, `nick`, `msg`, `uid`, `show_pic`,
            `time`,`ip`,`time_updated`
          FROM
            `the_data`
          ORDER BY `time_updated` DESC LIMIT 26340 ,15) d
    

    另外,请确保您在 time_updatedmsg_id 上有索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-27
      • 2021-04-15
      • 1970-01-01
      相关资源
      最近更新 更多