需要帮助优化连接的MySQL查询

时间:2012-03-25 11:54:00

标签: mysql sql explain query-tuning

我仍然无法理解如何阅读,理解和优化MySQL解释。我知道在orderby列上创建索引,但这就是它。因此,我希望你能帮助我调整这个问题:

EXPLAIN
SELECT specie.id, specie.commonname, specie.block_description, maximage.title,
       maximage.karma, imagefile.file_name, imagefile.width, imagefile.height,
       imagefile.transferred
FROM specie
INNER JOIN specie_map ON specie_map.specie_id = specie.id
INNER JOIN (
    SELECT *
    FROM image
    ORDER BY karma DESC
) AS maximage ON specie_map.image_id = maximage.id
INNER JOIN imagefile ON     imagefile.image_id = maximage.id
                        AND imagefile.type = 'small'
GROUP BY specie.commonname
ORDER BY commonname ASC
LIMIT 0 , 24 

这个查询的作用是找到具有最多业力的照片。你可以看到这个直播的结果:

http://www.jungledragon.com/species

我有物种表,图像表,中间的映射表和图像文件表,因为每个图像有多个图像文件(格式)。

解释输出:

enter image description here

对于specie表,我的主要ID和字段commonname都有索引。对于图像表,我在其id和karma字段上有索引,还有一些与此问题无关的索引。

此查询目前需要0.8到1.1秒,这在我看来太慢了。我怀疑正确的索引会加快这个速度,但我不知道哪一个。

3 个答案:

答案 0 :(得分:1)

如果你能提供表结构和索引会更好。我提出了这个替代方案,如果你能尝试这个并告诉我发生了什么会很好(我很好奇!):

SELECT t.*, imf.* FROM (
  SELECT s.*, (SELECT id FROM image WHERE karma = MAX(i.karma) LIMIT 1) AS max_image_id 
  FROM image i 
  INNER JOIN specie_map smap ON smap.image_id = i.id
  INNER JOIN specie s ON s.id = smap.specie_id
  GROUP BY s.commonname 
  ORDER BY s.commonname ASC
  LIMIT 24
) t INNER JOIN imagefile imf
ON t.max_image_id = imf.image_id AND imf.type = 'small' 

答案 1 :(得分:1)

真正的问题是没有必要优化MySQL解释。通常会有一个查询(或多个查询),您希望它是高效的,EXPLAIN是一种查看查询执行是否会按预期发生的方式。

那就是你需要了解执行计划的外观和原因,并将其与EXPLAIN命令的结果进行比较。要了解计划的外观,您应该了解how indexes in MySQL work

与此同时,你的查询是一个棘手的问题,因为对于使用它的有效索引有一些限制:a)同时排序和来自一个表的字段,和b)来自另一个表的finding the last element in each group(后者是一个棘手的任务本身)。由于您的数据库相当小,您很幸运,您当前的查询速度相当快(尽管您认为它很慢)。

我会以一种hacky方式重写查询(我假设每个物种至少有一个foto):

SELECT
   specie.id, specie.commonname, specie.block_description,
   maximage.title, maximage.karma,
   imagefile.file_name, imagefile.width, imagefile.height, imagefile.transferred
FROM (
    SELECT s.id,
           (SELECT i.id
            FROM specie_map sm
            JOIN image i ON sm.image_id = i.id
            WHERE sm.specie_id = s.id
            ORDER BY i.karma DESC
            LIMIT 1) as image_id
    FROM specie s
    ORDER BY s.commonname
    LIMIT 0, 24
) as ids
JOIN specie
  ON ids.id = specie.id
JOIN image as maximage
  ON maximage.id = ids.image_id
JOIN imagefile
  ON imagefile.image_id = ids.image_id AND imagefile.type = 'small';

您需要以下索引:

  • (commonname) specie
  • (specie_id, image_id)
  • 上的复合specie_map
  • (id, karma)
  • 上的复合image
  • (image_id, type)
  • 上的复合imagefile

现在应该在子查询中进行分页。

这个想法是在仅使用id操作的子查询中进行复杂计算,并在顶部连接其余数据。数据将按子查询结果的顺序排序。

答案 2 :(得分:1)

我认为通过摆脱子查询你会走得很好。查看“explain”结果的第一行和最后一行 - 它将整个“image”表复制到临时表中。您可以通过将子查询替换为INNER JOIN image并将ORDER BY karma DESC移至最终ORDER BY子句来获得相同的结果:

SELECT specie.id, specie.commonname, specie.block_description, maximage.title,
       maximage.karma, imagefile.file_name, imagefile.width, imagefile.height,
       imagefile.transferred
FROM specie
INNER JOIN specie_map ON specie_map.specie_id = specie.id
INNER JOIN image AS maximage ON specie_map.image_id = maximage.id
INNER JOIN imagefile ON     imagefile.image_id = maximage.id
                        AND imagefile.type = 'small'
GROUP BY specie.commonname
ORDER BY commonname ASC, karma DESC
LIMIT 0 , 24