2013-12-17 20 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的顺序存储,就像此查询需要它们一样。这将导致非常快速的排序(因为它们已经在索引中排序)。

相关问题