如何使用EXPLAIN显示'使用临时;来优化MySQL查询;使用filesort'

时间:2013-12-17 08:20:54

标签: mysql query-optimization temporary filesort

我有一个相当慢的查询,我想优化。 EXPLAIN显示'使用临时;使用filesort'。我尝试了一些解决方案,并且没有ORDER BY,甚至设法摆脱'使用filesort'。但有没有办法避免'暂时使用;完全使用filesort,而不牺牲ORDER BY?

这是我的疑问:

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 ...替换ORDER BY NULL时,'using filesort'消失了。之后我可以用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;

1 个答案:

答案 0 :(得分: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排序,就像此查询所需的一样。这将导致非常快速的排序(因为它们已经在索引中排序)。