Mysql 使用左联接和按…排序的查询速度较慢,使用文件排序

Mysql 使用左联接和按…排序的查询速度较慢,使用文件排序,mysql,left-join,filesort,Mysql,Left Join,Filesort,我有以下疑问: SELECT fruit.date, fruit.name, fruit.reason, fruit.id, fruit.notes, food.name FROM fruit LEFT JOIN food_fruits AS ff ON fruit.fruit_id = ff.fruit_id AND ff.type='fruit' LEFT JOIN food USING (food_id) LE

我有以下疑问:

SELECT 
    fruit.date,
    fruit.name,
    fruit.reason,
    fruit.id,
    fruit.notes,
    food.name
FROM
    fruit
 LEFT JOIN
    food_fruits AS ff ON fruit.fruit_id = ff.fruit_id AND ff.type='fruit'
 LEFT JOIN
    food USING (food_id)
 LEFT JOIN
    fruits_sour AS fs ON fruits.id = fs.fruit_id
WHERE
    (fruit.date < DATE_SUB(NOW(), INTERVAL 180 DAY))
        AND (fruit.`status` = 'Rotten')
        AND (fruit.location = 'USA')
        AND (fruit.size = 'medium')
        AND (fs.fruit_id IS NULL)
ORDER BY `food.name` asc
LIMIT 15 OFFSET 0
我甚至有一些我认为会更好用的索引,但没有被使用:

fruit        - fruit_filter (size, status, location, date)
food_fruits  - food_type (type)
food         - food (id)
fruits_sour  - fruit_id (fruit_id)
food_fruits  - fruit_key (fruit_id, type)
food         - id_name (food_id, name)
不幸的是,ORDERBY子句导致使用临时表和文件排序。否则,查询将运行lickety split。如何使此查询不需要进行文件排序?我错过了什么

编辑:

委员会解释说:

原因是您的ORDER BY子句在不属于此查询所用索引的字段上执行。引擎可以使用fruit_filter索引运行查询,但是它必须在不同的字段上进行排序,这时filesort开始发挥作用,这基本上意味着不使用索引进行排序,这要感谢注释中的提醒

我不知道您得到的结果是什么,但是如果差异很大,那么我将创建一个带有中间结果的临时表,然后对其进行排序

顺便说一下,我不知道为什么你用左连接而不是内连接,为什么你用食物水果-在评论中回答

更新

尝试子查询方法,可能未经测试,它将排序与预筛选分离:

SELECT
    fr.date,
    fr.name,
    fr.reason,
    fr.id,
    fr.notes,
    food.name
FROM
  (
  SELECT 
    fruit.date,
    fruit.name,
    fruit.reason,
    fruit.id,
    fruit.notes,
  FROM
    fruit
  LEFT JOIN
    fruits_sour AS fs ON fruit.id = fs.fruit_id
  WHERE
    (fruit.date < DATE_SUB(NOW(), INTERVAL 180 DAY))
        AND (fruit.`status` = 'Rotten')
        AND (fruit.location = 'USA')
        AND (fruit.size = 'medium')
        AND (fs.fruit_id IS NULL)
  ) as fr
LEFT JOIN
    food_fruits AS ff ON fr.fruit_id = ff.fruit_id AND ff.type='fruit'
LEFT JOIN
    food USING (food_id)  
ORDER BY `food.name` asc
LIMIT 15 OFFSET 0
您的订单由。。。你知道,限制条款需要一些排序。优化性能的诀窍是按。。。限制最小的列集,然后基于所选的15行构建完整的结果集。因此,让我们在子查询中尝试一组最小的列

     SELECT fruit.id,
            food.name
       FROM fruit
  LEFT JOIN food_fruits AS ff   ON fruit.fruit_id = ff.fruit_id 
                               AND ff.type='fruit'
  LEFT JOIN food USING (food_id)
  LEFT JOIN fruits_sour AS fs ON fruits.id = fs.fruit_id
      WHERE fruit.date < DATE_SUB(NOW(), INTERVAL 180 DAY)
        AND fruit.`status` = 'Rotten'
        AND fruit.location = 'USA'
        AND fruit.size = 'medium'
        AND fs.fruit_id IS NULL
   ORDER BY food.name ASC
      LIMIT 15 OFFSET 0

你知道这是怎么回事吗?在子查询中,您只需要使用足够的数据来标识要检索的行的一小部分。然后,将该子查询连接到主表以提取所有数据。限制必须排序的内容的行长度有助于提高性能,因为MySQL可以在其排序缓冲区中对其进行排序,而不必执行更复杂、更慢的排序/合并操作。但是,你无法通过解释来判断它是否会这样做。

你能在此查询上运行解释并发布输出吗?@Ashalynd是的,尽管格式可能有点奇怪。food\u fruits是水果和食物之间的链接关联表。我使用左连接是因为我需要水果中的所有行,但并非所有水果都必须链接到食物。子查询似乎可以提高7-8%的效率。虽然这很好,我也很感激,但如果能够获得更大的性能提升,那就太好了。我仍然不能完全确定为什么id\u name索引不能用于排序部分……小心,filesort并不是你所认为的那样。好的,我会修正答案的。我在写了13个小时后编辑了这篇文章,建议在索引中添加一个内容。
SELECT fruit.date,
       fruit.name,
       fruit.reason,
       fruit.id,
       fruit.notes,
       list.name
  FROM fruit
  JOIN (
               SELECT fruit.id,
                      food.name
                 FROM fruit
            LEFT JOIN food_fruits AS ff    ON fruit.fruit_id = ff.fruit_id
                                          AND ff.type='fruit'
            LEFT JOIN food USING (food_id)
            LEFT JOIN fruits_sour AS fs ON fruits.id = fs.fruit_id
                WHERE fruit.date < DATE_SUB(NOW(), INTERVAL 180 DAY)
                  AND fruit.`status` = 'Rotten'
                  AND fruit.location = 'USA'
                  AND fruit.size = 'medium'
                  AND fs.fruit_id IS NULL
             ORDER BY food.name ASC
                LIMIT 15 OFFSET 0
       ) AS list ON fruit.id = list.id
 ORDER BY list.name