簡體   English   中英

查詢LEFT JOIN和ORDER BY ... LIMIT slow,使用Filesort

[英]query with LEFT JOIN and ORDER BY…LIMIT slow, uses 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)
 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)

不幸的是, ORDER BY子句導致temporary表和filesort被使用。 沒有它,查詢運行lickety-split。 如何讓這個查詢不需要filesort 我錯過了什么?

編輯:

解釋: 解釋

原因是你的ORDER BY子句在字段上完成,該子字段不是用於此查詢的索引的一部分。 引擎可以使用fruit_filter索引運行查詢,但是它必須對不同的字段進行排序,這就是當filesort進入游戲時(這基本上意味着“不使用索引排序”,這要歸功於評論中的提醒)。

我不知道你得到的結果是什么時候,但如果差別很大,那么我會創建一個包含中間結果的臨時表,然后對其進行排序。

(順便說一句,我不知道你為什么使用LEFT JOIN而不是INNER JOIN ,為什么要使用food_fruits - 在評論中回答)

UPDATE。

嘗試子查詢方法,可能是(未經測試),它將排序與預過濾分開:

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

你知道,你的ORDER BY ... LIMIT子句需要一些排序。 優化性能的技巧是ORDER BY ... LIMIT最小的列集,然后根據選擇的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

此查詢為您提供十五個頂級ID及其名稱。

我會在現有的fruit_filter索引的末尾添加id來給出(size, status, location, date, id) 這將使其成為覆蓋索引復合 ,並允許您的過濾查詢完全從索引中得到滿足。

除此之外,使用更多或不同的索引來優化它是很困難的,因為大部分查詢都是由其他因素驅動的,例如您已應用的LEFT JOIN ... IS NULL join-fail准則。

然后,您可以將此子查詢加入到水果表中以獲取完整的結果集。

完成之后,這將是這樣的。

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

你知道這是怎么回事嗎? 在子查詢中,您只需要足夠的數據來識別要檢索的行的哪個微小子集。 然后,將該子查詢加入主表以提取所有數據。 限制你必須排序的東西的行長度有助於提高性能,因為MySQL可以將它排序為排序緩沖區,而不是必須進行更精細和更慢的排序/合並操作。 (但是,你無法告訴EXPLAIN它是否會這樣做。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM