2013-12-17 41 views
1

我有一個相當慢的查詢,我想優化。 EXPLAIN顯示'使用臨時;使用filesort'。我嘗試了一些解決方案,沒有ORDER BY,甚至設法擺脫'使用filesort'。但是有沒有辦法避免'臨時使用;使用filesort'完全不犧牲ORDER BY?如何使用EXPLAIN優化MySQL查詢,顯示'using temporary;使用filesort'

這是我的查詢:

SELECT `tags`.`name`, 
     `tags`.`tag_id`, 
     COUNT(*) AS `qty_products` 
    FROM `products_subsubcategories` 
     JOIN `products_tags` ON `products_subsubcategories`.`product_id` = `products_tags`.`product_id` 
     JOIN `products` ON `products_subsubcategories`.`product_id` = `products`.`product_id` 
     JOIN `tags` ON `products_tags`.`tag_id` = `tags`.`tag_id` 
    WHERE  `products_subsubcategories`.`subsubcategory_id` = 55 
      AND `tags`.`type` = 'brand' 
      AND `products`.`dont_display` = 0 
    GROUP BY `tags`.`tag_id` 
    ORDER BY `tags`.`order`, 
      `tags`.`name`; 

的subsubcategory 55是動態的用戶輸入。

這是EXPLAIN結果:

id select_type  table      type possible_keys    key     key_len  ref           rows filtered Extra 
1 SIMPLE   products_subsubcategories ref  PRIMARY,subsubcategory_id subsubcategory_id 4   const          3982 100.00  Using temporary; Using filesort 
1 SIMPLE   tags      ALL  PRIMARY,type    NULL    NULL  NULL          679  78.94  Using where; Using join buffer 
1 SIMPLE   products     eq_ref PRIMARY,dont_display  PRIMARY    4   mbb.products_subsubcategories.product_id 1  100.00  Using where 
1 SIMPLE   products_tags    eq_ref PRIMARY,tag_id    PRIMARY    8   mbb.products.product_id,mbb.tags.tag_id  1  100.00  Using where; Using index 

(當我通過ORDER BY NULL更換ORDER BY ...,在「使用文件排序」 disapperars我可以用PHP的結果之後進行排序,儘管它與MySQL更方便,當然。 ...)

我的表是這樣的:

CREATE TABLE IF NOT EXISTS `products_subsubcategories` (
    `position` smallint(5) unsigned NOT NULL DEFAULT '0', 
    `product_id` int(10) unsigned NOT NULL, 
    `subsubcategory_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`product_id`,`subsubcategory_id`), 
    KEY `subsubcategory_id` (`subsubcategory_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE IF NOT EXISTS `products_tags` (
    `product_id` int(10) unsigned NOT NULL, 
    `tag_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`product_id`,`tag_id`), 
    KEY `tag_id` (`tag_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE IF NOT EXISTS `products` (
    `article_number` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `date` date DEFAULT NULL, 
    `delivery_time` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `description` text COLLATE utf8_unicode_ci NOT NULL, 
    `dont_display` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `ean` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `image_error` tinyint(1) NOT NULL DEFAULT '0', 
    `image_is_downloaded` tinyint(1) NOT NULL DEFAULT '0', 
    `image_url` varchar(400) COLLATE utf8_unicode_ci NOT NULL, 
    `image_url_170_134` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `image_url_original_size` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `is_duplicate` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `is_not_associated_to_category` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `is_not_associated_to_subcategory` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `is_not_associated_to_subsubcategory` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `last_association` datetime DEFAULT NULL, 
    `last_completion_by_ean` datetime DEFAULT NULL, 
    `matching_age` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_brand` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_category` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_color` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_gender` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_keywords` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_main_category` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_size` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_subcategory` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `matching_subsubcategory` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `old_price` decimal(7,2) unsigned NOT NULL DEFAULT '0.00', 
    `price` decimal(7,2) unsigned NOT NULL DEFAULT '0.00', 
    `product_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `product_list_id` int(10) unsigned NOT NULL DEFAULT '0', 
    `qty_overall_clicks` int(10) unsigned NOT NULL DEFAULT '0', 
    `shipping` decimal(7,2) unsigned NOT NULL DEFAULT '0.00', 
    `shop_url` varchar(400) COLLATE utf8_unicode_ci NOT NULL, 
    `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `vendor_id` int(10) unsigned NOT NULL DEFAULT '0', 
    PRIMARY KEY (`product_id`), 
    KEY `article_number` (`article_number`), 
    KEY `dont_display` (`dont_display`), 
    KEY `ean` (`ean`), 
    KEY `is_deleted` (`is_deleted`), 
    KEY `is_duplicate` (`is_duplicate`), 
    KEY `is_not_associated_to_category` (`is_not_associated_to_category`), 
    KEY `is_not_associated_to_subcategory` (`is_not_associated_to_subcategory`), 
    KEY `is_not_associated_to_subsubcategory` (`is_not_associated_to_subsubcategory`), 
    KEY `product_list_id` (`product_list_id`), 
    KEY `vendor_id` (`vendor_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1084370; 

CREATE TABLE IF NOT EXISTS `tags` (
    `display_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `image_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `order` int(10) unsigned NOT NULL DEFAULT '0', 
    `tag_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `type` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`tag_id`), 
    KEY `type` (`type`), 
    KEY `name` (`name`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1084; 
+0

您可以嘗試將標記作爲第一個表,然後通過使用STRAIGHT_JOIN而不是INNER JOIN(可能會添加以利用標記順序字段)來強制連接的順序。但是,這是否會有所幫助將取決於表格上的行數 – Kickstart

+0

@Kickstart我試過了,查看EXPLAIN的'rows'列中的數字,它至少有一點幫助。它不會刪除'使用臨時;儘管使用filesort'。但是,儘管如此,感謝這個想法,任何使這個查詢更快的是歡迎:-) – Hendrik

+0

嘗試一個覆蓋索引的類型,然後命令,然後命名標籤表。 – Kickstart

回答

0

我建議嘗試不使用JOIN的查詢,只是因爲除了獲得計數之外,您不使用JOIN。

嘗試以下操作:

SELECT 
t.name, 
t.tag_id, 
(
    SELECT COUNT(*) FROM product_tags pt 
    INNER JOIN product_subcategories ps 
    ON ps.product_id = pt.product_id 
    INNER JOIN product p 
    ON p.product_id = pt.product_id 
    WHERE pt.tag_id = t.tag_id 
    AND p.dont_display = 0 
    AND ps.subsubcategory_id = 55 
) AS qty_products 
FROM tags t 
WHERE 
t.type = 'brand' 
AND EXISTS (
    SELECT * FROM product_tags pt 
    INNER JOIN product_subcategories ps 
    ON ps.product_id = pt.product_id 
    INNER JOIN product p 
    ON p.product_id = pt.product_id 
    WHERE pt.tag_id = t.tag_id 
    AND p.dont_display = 0 
    AND ps.subsubcategory_id = 55 
) 
ORDER BY t.order,t.name 

這樣,你只查詢tags表,得到結果反饋,以便開始。然後,對於每條記錄,您檢查是否存在subsubcategory 55,否則跳過該標籤。

這應該大大提高您的查詢,除非有標籤(即使如此,它仍可能改善的事情。)

另一個改進可以使是一個的Kickstart的意見提出了巨大的號碼:添加覆蓋索引標籤表:

ALTER TABLE tags 
ADD INDEX `type_order_name` (`type`,`order`,`name`) 

如果你不熟悉多鍵,只知道他們在內部有效地存儲爲每一列的串聯,在順序列在列關鍵定義。

因此,只要您在WHERE子句中提供type,標籤將按照ordername的順序存儲,就像此查詢需要它們一樣。這將導致非常快速的排序(因爲它們已經在索引中排序)。

相關問題