【问题标题】:Simple MySQL query runs very slow简单的 MySQL 查询运行速度很慢
【发布时间】:2012-11-20 13:13:30
【问题描述】:

我在 MySQL 中发现了一些奇怪的(对我而言)行为。我有一个简单的查询:

SELECT CONVERT( `text`.`old_text` 
USING utf8 ) AS stext
FROM  `text` 
WHERE  `text`.`old_id` IN 
(    
 SELECT  `revision`.`rev_text_id`
 FROM  `revision` 
 WHERE  `revision`.`rev_id`
 IN 
  (    
  SELECT `page_latest`
  FROM  `page` 
  WHERE `page_id` = 108
  )
)

当我运行它时,phpmyadmin 显示执行时间为 77.0446 秒。
但后来我替换了

WHERE  `text`.`old_id` IN 

通过

WHERE  `text`.`old_id` = 

它的执行时间下降到大约 0.001 秒。本次查询结果

 SELECT  `revision`.`rev_text_id`
 FROM  `revision` 
 WHERE  `revision`.`rev_id`
 IN 
  (    
  SELECT `page_latest`
  FROM  `page` 
  WHERE `page_id` = 108
  )    

+------------+
|rev_text_id |
+------------+
|6506        |
+------------+

有人可以解释一下这种行为吗?

【问题讨论】:

  • 不,但我确定EXPLAIN 可以。
  • @Quadrat137 - 出于好奇,您的最后执行时间是多少?在 77 秒内节省了多少?
  • 是的,你的查询是 0.001,和 Kuya John 的一样,非常感谢你们俩

标签: mysql sql phpmyadmin


【解决方案1】:

尝试在以下列添加INDEX

ALTER TABLE `text` ADD INDEX idx_text (old_id);
ALTER TABLE `revision` ADD INDEX idx_revision (rev_text_id);

并执行以下查询

SELECT  DISTINCT CONVERT(a.`old_text` USING utf8 ) AS stext
FROM    `text` a
        INNER JOIN `revision` b
            ON a.`old_id` = b.`rev_text_id`
        INNER JOIN `page` c
            ON b.`rev_id` = c.`page_latest`
WHERE   c.`page_id` = 108

PS:您能否同时运行以下查询并发布它们各自的结果?

DESC `text`;
DESC `revision`;
DESC `page`;

【讨论】:

    【解决方案2】:

    您可以通过两种主要方式来提高查询性能

    • 添加索引(如 Kuya 提到的)
    • 尽可能摆脱子查询

    对于索引,在要搜索匹配项的列上添加索引: text.old_id, revision.rev_text_id & page.page_id

    ALTER TABLE `text` ADD INDEX idx_text (old_id);
    ALTER TABLE `revision` ADD INDEX idx_revision (rev_text_id);
    ALTER TABLE `page` ADD INDEX idx_page (page_id);
    

    您的下一个问题是嵌套子选择会影响您的查询执行计划。这是一个讨论JOIN vs Subquery 的好帖子。这是一篇关于如何从 mySQL 中get execution plan info 的文章。

    首先查看执行计划可能会令人困惑,但当您必须关注查询优化时,它将是您最好的朋友。

    这是一个仅使用联接的相同查询的示例(您可以使用内部或左侧并获得几乎相同的结果)。我没有你的表格或数据,所以请原谅语法问题(我无法验证代码在你的环境中逐字运行,但它应该给你一个很好的起点)。

    SELECT 
      CONVERT( `text`.`old_text` USING utf8 ) AS stext
      FROM  `text` 
      -- inner join only returns rows when it can find a 
      --    matching `revision`.`rev_text_id` row to `text`.`old_id`
      INNER JOIN `revision`  
      ON `text`.`old_id` = `revision`.`rev_text_id` 
      -- Inner Join only returns rows  when it can find a 
      --  matching `page_latest` row to `page_latest`
      INNER JOIN `page`
      ON `revision`.`rev_id` = `page`.`page_latest`
    WHERE `page`.`page_id` = 108
    

    【讨论】:

      【解决方案3】:

      MySQLDB 循环遍历内部查询的每个结果,并将其与外部查询中的每条记录进行比较。 在第二个内部查询中;

         WHERE  `revision`.`rev_id`
         IN 
         ( SELECT `page_latest`
           FROM  `page` 
           WHERE `page_id` = 108
      

      您绝对应该使用 '=' 而不是 IN,因为您选择的是不同的记录,当您知道每次只会返回一条记录时,循环遍历结果是没有意义的

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        相关资源
        最近更新 更多