我有一个相当慢的查询,我想优化。 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;
答案 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
,标记将按order
和name
排序,就像此查询所需的一样。这将导致非常快速的排序(因为它们已经在索引中排序)。