Медленный запрос SELECT ... GROUP BY по двум полям таблиц` (MySQL / InnoDB) - PullRequest
0 голосов
/ 18 июля 2011

У меня есть запрос на выбор заказов, и для каждого заказа мне нужно получить список продуктов, который он включает с итоговым количеством для каждого продукта.

В настоящее время это очень медленно (10 секунд на моей установке), Мне нужно ускорить его.

Обновление: переключение на MyISAM не вариант, мне нужны транзакции

На данный момент такой запрос:

SELECT `Order`.*, `Product`.`id`, `Product`.`msrp`, SUM(`OrderItem`.`quantity`) AS sum
FROM `orders` AS `Order` 
LEFT JOIN `order_items` AS `OrderItem` ON (`OrderItem`.`order_id` = `Order`.`id`)
LEFT JOIN `product_variations` AS `ProductVariation` ON (`OrderItem`.`product_variation_id` = `ProductVariation`.`id`)
LEFT JOIN `products` AS `Product` ON (`ProductVariation`.`product_id` = `Product`.`id`)
WHERE 1 = 1
GROUP BY `Order`.`id`, `Product`.`id`
ORDER BY `Order`.`created` DESC
LIMIT 20;

Объяснение:

explain results

Схема (усечена, оставлены только обязательные поля):

CREATE TABLE `orders` (
  `id` int(11) NOT NULL auto_increment,
  `created` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `customer_comments` text NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `created` (`created`),
  KEY `id_created` (`id`,`created`),
) ENGINE=InnoDB 

CREATE TABLE `order_items` (
  `id` int(11) NOT NULL auto_increment,
  `order_id` int(11) NOT NULL,
  `product_variation_id` int(11) NOT NULL,
  `type` enum('First','Second') default NULL,
  `quantity` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `item_UNIQUE` (`order_id`,`product_variation_id`,`type`),
  KEY `fk_order_items_product_variations1` (`product_variation_id`),
  CONSTRAINT `fk_order_items_orders1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_order_items_product_variations1` FOREIGN KEY (`product_variation_id`) REFERENCES `product_variations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB

CREATE TABLE `product_variations` (
  `id` int(11) NOT NULL auto_increment,
  `product_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  CONSTRAINT `fk_product_variations_products1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB 

CREATE TABLE `products` (
  `id` int(11) NOT NULL auto_increment,
  `msrp` decimal(5,2) NOT NULL,
  PRIMARY KEY  (`id`),
) ENGINE=InnoDB

Сервер MySQL 5.0.77, таблицы InnoDB.orders - это около 75 тыс. Записей, order_items 160 тыс. Записей, product_variations 140 тыс. Записей, product - 300 записей.

Любая помощь приветствуется.

Ответы [ 2 ]

1 голос
/ 18 июля 2011

Хотя ваш запрос выглядит хорошо структурированным и объединенным, проблема может заключаться в объеме записей.У вас есть группировка по месту и т. Д. Я бы добавил предложение ONE ...

SELECT STAIGHT_JOIN ... остальная часть запроса.

STRAIGHT_JOIN говорит движку сделать это в том порядке, в каком выВместо этого мы заявили, что пытаемся оптимизировать меньшие таблицы в качестве основных, а затем объединяем их в обратном порядке.Я видел, как STRAIGHT_JOIN показывает потрясающую производительность в НЕСКОЛЬКИХ других областях ... возможно, это может помочь и вам.

1 голос
/ 18 июля 2011

В первую очередь я бы попытался заменить SUM на предварительно вычисленную таблицу product_sum. Известно, что InnoDB медленнее с агрегатными функциями (особенно с 75k + строками!). Вы действительно не хотите вычислять эту сумму каждый запрос (если данные не изменяются очень часто).

Может быть полезно использовать триггер, который пересчитывает таблицу product_sum после вставки / обновления заказов ... но не совсем понятно, каков контекст этого запроса (выполняется ли он конечным пользователем? запрос или отчет? и т. д.).

Другим вариантом является переключение движка на MyISAM (что намного быстрее с агрегатами), однако огромный минус - потеря транзакций и ограничения внешнего ключа.

...