簡體   English   中英

MySQL使用Using臨時排序; 使用文件排序

[英]MySQL sorting with Using temporary; Using filesort

這是我要啟動的查詢:

SELECT c.creative_id, c.creative_title, c.creative_image_name, c.gravity, c.ad_strength
FROM creatives AS c
INNER JOIN term_relationships AS tr ON c.creative_id = tr.creative_id
WHERE tr.term_id
IN ( 14, 1, 50, 76, 104 )
GROUP BY c.creative_id
HAVING COUNT(tr.term_id ) =5
ORDER BY c.gravity ASC 
LIMIT 30;

這是此查詢的EXPLAIN輸出:

在此處輸入圖片說明

這是creatives表結構:

CREATE TABLE `creatives` (
  `creative_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `scraper_id` bigint(20) unsigned DEFAULT NULL,
  `creative_title` varchar(255) NOT NULL,
  `creative_image_name` varchar(255) DEFAULT NULL,
  `image_attrib` varchar(12) DEFAULT NULL,
  `original_image_name` varchar(255) DEFAULT NULL,
  `creative_subtext` varchar(255) DEFAULT NULL,
  `dest_url` varchar(2083) NOT NULL,
  `lp_url` varchar(2083) NOT NULL,
  `lp_image_name` varchar(255) DEFAULT NULL,
  `lp_image_flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `creative_first_seen` date NOT NULL,
  `creative_last_seen` date NOT NULL,
  `daily_ad_count` int(5) unsigned NOT NULL,
  `ad_strength` int(11) unsigned NOT NULL,
  `prev_ad_strength` int(11) unsigned DEFAULT NULL,
  `gravity` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`creative_id`),
  KEY `gravity` (`gravity`)
) ENGINE=InnoDB AUTO_INCREMENT=173037591 DEFAULT CHARSET=utf8

我擔心Using temporary; using filesort 在另一列上同時使用GROUP BYORDER BY啟動時,請Using temporary; using filesort 如果刪除ORDER BY ,則臨時和文件排序將消失,查詢運行會非常快。

我不明白的是,為什么mysql需要臨時表,為什么不能先在filter +按c.gravity排序,然后將結果表分組並根據HAVING子句進行過濾。 過濾后的表格將按c.gravity正確排序,因為重力值在分組並具有過濾器后保持不變。

我試過的

  1. 選擇沒有ORDER BY所有內容,將其包裝到子查詢中,然后再次加入到creatives表中-使用臨時,文件排序和緩慢的結果相同

  2. 試圖添加FORCE USE INDEX FOR ORDER BY (gravity)並且它沒有任何改變。 EXPLAIN和執行時間保持不變。

更新 :問題已由@Rick回答,並且使用他的相關子查詢並且不使用GROUP BY確實更快。 我在這里為查詢添加EXPLAIN輸出:

在此處輸入圖片說明

以及帶有新創建索引的SHOW CREATE TABLE term_relationships的輸出:

在此處輸入圖片說明

@Rick還有一個問題:為什么我們需要用c3進行外部查詢? 似乎僅僅是再加入一個creatives ,只是為了從其他列中獲取值並通過重力對記錄進行排序。 但是,它們已經使用內部查詢進行了排序,我們可以輕松地在c1添加缺少的列,從而實現:

SELECT  c1.creative_id,c1.creative_title,c1.creative_image_name,c1.gravity, c1.ad_strength
            FROM  creatives AS c1
            WHERE  
              ( SELECT  COUNT(*)
                    FROM  term_relationships
                    WHERE  c1.creative_id = creative_id
                      AND  term_id IN ( 14, 1, 50, 76, 104 )
              ) = 5 
            ORDER BY  c1.gravity ASC
            LIMIT  30;

我的理解正確嗎,或者我在查詢中遺漏了什么?

臨時表和文件排序本身不是反派。 它們是如此龐大。

這可能看起來更復雜,但是可能更快:

SELECT  c3.creative_id,
        c3.creative_title, c3.creative_image_name,
        c3.gravity, c3.ad_strength
    FROM  
      ( SELECT  creative_id
            FROM  creatives AS c1
            WHERE  
              ( SELECT  COUNT(*)
                    FROM  term_relationships
                    WHERE  c1.creative_id = creative_id
                      AND  term_id IN ( 14, 1, 50, 76, 104 )
              ) = 5 
            ORDER BY  c1.gravity ASC
            LIMIT  30
      ) AS c2
    JOIN  creatives c3 USING (creative_id)
    ORDER BY  c3.gravity 

如果碰巧對內部查詢使用INDEX(gravity) ,則它將在找到具有全部5個事務的30行后停止。 如果它生成一個tmp表,它將只有30行-比原始查詢要好得多。 另請注意,tmp表將更窄-僅creative_id在其中。 最后,它返回到creatives以獲取其余所需的列。 最后,將有另一種排序方式,但只有30行。

此外,“文件排序”在RAM中通常是非常快速的排序,而不是真正的“文件”排序。 我很確定我的查詢不會在磁盤上。

term_relationships需要以下綜合索引: INDEX(creative_id, term_id)

暫無
暫無

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

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