Cómo optimizar una consulta MySQL con EXPLAIN mostrando 'using temporary; usando filesort '

Tengo una consulta bastante lenta que me gustaría optimizar. EXPLAIN muestra 'usando temporal; usando filesort '. Intenté algunas soluciones y, sin un ORDER BY, incluso pude deshacerme de la 'utilización del filesort'. Pero, ¿hay alguna forma de evitar el uso temporal? usando filesort 'completamente, sin sacrificar el ORDER BY?

Esta es mi consulta:

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`;

La subcategoría 55 es la entrada dinámica del usuario.

Este es el resultado 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

(Cuando reemplazo ORDER BY ... por ORDER BY NULL , el 'using filesort' decepciona. Pude ordenar el resultado con PHP luego, aunque es más conveniente con MySQL , por supuesto ...)

Mis tablas se ven así:

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
@Kickstart Intenté esto, y mirando los números en la columna 'rows' de EXPLAIN, ayuda al menos un poco. No elimina 'Uso temporal; Sin embargo, el uso de filesort '. Pero gracias por la idea, no obstante, cualquier cosa que haga que esta consulta sea más rápida es bienvenida :-)
agregado el autor Hendrik, fuente
Podría tratar de poner etiquetas como la primera tabla, y luego forzar el orden de las uniones utilizando STRAIGHT_JOIN en lugar de INNER JOIN (quizás agregando a para utilizar el campo de orden de las etiquetas). Pero si esto ayudará dependerá del número de filas en las tablas, aunque
agregado el autor Kickstart, fuente
Pruebe con un índice de cobertura sobre el tipo, luego solicite el nombre en la tabla de etiquetas.
agregado el autor Kickstart, fuente

1 Respuestas

Sugiero que intentes una consulta que no utilice JOINs, simplemente por el hecho de que no usas los JOIN para nada más que obtener un conteo.

Pruebe lo siguiente:

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

De esta manera, solo consultas la tabla tags , obteniendo los resultados nuevamente en orden inicialmente. Luego, para cada registro, verifique si alguno está en subsubcategory 55, y omita esa etiqueta.

Esto debería mejorar su consulta en gran medida, a menos que haya una enorme cantidad de etiquetas (y aun así, podría mejorar las cosas).

Otra mejora que puede realizar es el único Kickstart sugerido en los comentarios: agregue un índice de cobertura a la tabla de etiquetas:

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

Si no está familiarizado con las claves multiparte, simplemente sepa que internamente se almacenan efectivamente como una concatenación de cada una de las columnas, en el orden en que se enumeran las columnas en la definición de clave.

Por lo tanto, siempre que proporcione el tipo en la cláusula WHERE, las etiquetas se almacenarán ordenadas por y name , tal como esta consulta quiere ellos. Esto dará como resultado una clasificación muy rápida (porque ya estarán clasificados en el índice).

0
agregado