2017-02-22 11 views
1

Добрый день, я все время сумасшедший, выясняя, что я должен делать с моим запросом и/или с моей структурой таблиц, чтобы улучшить запрос, чтобы получить лучших продавцов, которые запускаются в течение более 1 сек.GROUP BY + ORDER BY сделайте мой запрос очень медленным

Вот запрос, я говорю:

SELECT pr.id_prod, MAX(pr.stock) AS stock, MAX(pr.dt_add) AS dt_add, SUM(od.quantity) AS quantity 
    FROM orders AS o 
    INNER JOIN orders_details AS od ON od.id_order = o.id_order 
    INNER JOIN products_references AS pr ON pr.id_prod_ref = od.id_prod_ref 
    INNER JOIN products AS p ON p.id_prod = pr.id_prod 
    WHERE o.id_order_status > 11 
    AND pr.active = 1 
    GROUP BY p.id_prod 
    ORDER BY quantity 
    LIMIT 10 

Если я использую GROUP BY p.id_prod вместо GROUP BY pr.id_prod и удалить ORDER BY, запрос выполняется в 0.07sec.

- это таблица EXPLAIN OKAY?

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE o range PRIMARY,id_order_status id_order_status 1  75940 Using where; Using index; Using temporary; Using filesort 
1 SIMPLE od ref id_order,id_prod_ref id_order 4 dbname.o.id_order 1 
1 SIMPLE pr eq_ref PRIMARY,id_prod PRIMARY 4 dbname.od.id_prod_ref 1 Using where 
1 SIMPLE p eq_ref PRIMARY,name_url,id_brand,name PRIMARY 4 dbname.pr.id_prod 1 Using index 

И это EXPLAIN без ORDER BY

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE p index PRIMARY,name_url,id_brand,name PRIMARY 4  1 Using index 
1 SIMPLE pr ref PRIMARY,id_prod id_prod 4 dbname.p.id_prod 2 Using where 
1 SIMPLE od ref id_order,id_prod_ref id_prod_ref 4 dbname.pr.id_prod_ref 67 
1 SIMPLE o eq_ref PRIMARY,id_order_status PRIMARY 4 dbname.od.id_order 1 Using where 

А вот табличные структуры

CREATE TABLE `orders` (
`id_order` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`id_dir` int(10) unsigned DEFAULT NULL, 
`id_status` tinyint(3) unsigned NOT NULL DEFAULT '11', 
PRIMARY KEY (`id_order`), 
KEY `id_dir` (`id_dir`), 
KEY `id_status` (`id_status`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 


CREATE TABLE `orders_details` (
`id_order_det` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`id_order` int(10) unsigned NOT NULL, 
`id_prod_ref` int(10) unsigned NOT NULL, 
`quantity` smallint(5) unsigned NOT NULL DEFAULT '1', 
PRIMARY KEY (`id_order_det`), 
UNIQUE KEY `id_order` (`id_order`,`id_prod_ref`) USING BTREE, 
KEY `id_prod_ref` (`id_prod_ref`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 


CREATE TABLE `products` (
`id_prod` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`name` varchar(60) COLLATE utf8_unicode_ci NOT NULL, 
PRIMARY KEY (`id_prod`), 
FULLTEXT KEY `name` (`name`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 


CREATE TABLE `products_references` (
`id_prod_ref` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`id_prod` int(10) unsigned NOT NULL, 
`stock` smallint(6) NOT NULL DEFAULT '0', 
`dt_add` datetime DEFAULT NULL, 
`active` tinyint(1) NOT NULL DEFAULT 0, 
PRIMARY KEY (`id_prod_ref`), 
KEY `id_prod` (`id_prod`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Я также пытался дать вам таблицы отношений (ON UPDATE, ON DELETE CASCADE, ...), но не удалось экспортировать его. Но я не думаю, что это важно сейчас!

Большое спасибо за вашу помощь,

Приветствия

+0

Зачем группировать столбец не в SELECT? GROUP BY p.id_prod - правильный путь. – Mihai

+0

Спасибо, что за ваш быстрый ответ тоже :) Ну, даже если я использую pr.id_prod в SELECT, он ничего не изменит:/ – Websphere

+0

ОК, поставьте EXPLAIN перед тем, как сделать запрос и отредактируйте ваш вопрос с результатами – Mihai

ответ

0

Попробуйте использовать имя псевдонима в порядке, а не значения из таблицы

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

SELECT p.id_prod, p.name, SUM(od.quantity) AS quantity 
FROM orders AS o 
INNER JOIN orders_details AS od ON od.id_order = o.id_order 
INNER JOIN products_references AS pr ON pr.id_prod_ref = od.id_prod_ref 
INNER JOIN products AS p ON p.id_prod = pr.id_prod 
WHERE pr.active = 1 
GROUP BY p.id_prod 
ORDER BY quantity 
LIMIT 10 

не забудьте использовать appropria т.е индексы на столбцы

+0

спасибо scaisEdge за ваш очень быстрый ответ :) Извините, что я плохой, я уже пользуюсь псевдонимом. Я исправил свой первоначальный пост. – Websphere

+0

OK, но используя p.id_prod, вы должны улучшить свою производительность, потому что вам не нужно извлекать значения pr.id_prod ... и, наконец, вам нужен соответствующий индекс в столбцах соединения. – scaisEdge

+0

действительно, используя p.id_prod в GROUP BY, значительно улучшает производительность, когда нет ORDER BY, но, к сожалению, я до сих пор не понимаю, почему! все столбцы, используемые в JOIN, индексируются. Я использую innoDB и устанавливаю все транзакции между индексированными столбцами. – Websphere

0

(переписано после OP добавил подробнее.)

SELECT pr.id_prod, 
     MAX(pr.stock) AS max_stock, 
     MAX(pr.dt_add) AS max_dt_add 
     SUM(od.quantity) AS sum_quantity 
    FROM orders AS o 
    INNER JOIN orders_details AS od 
      ON od.id_order = o.id_order 
    INNER JOIN products_references AS pr 
      ON pr.id_prod_ref = od.id_prod_ref 
    WHERE o.id_order_status > 11 
     AND pr.active = 1 
    GROUP BY pr.id_prod 
    ORDER BY sum_quantity 
    LIMIT 10 

Обратите внимание, что p был удален как не имеют значение.

Остерегайтесь SUM() при использовании JOIN с GROUP BY - вы может получить неправильное, надутом, значение.

Улучшение на одном столе:

CREATE TABLE `orders_details` (
`id_order` int(10) unsigned NOT NULL, 
`id_prod_ref` int(10) unsigned NOT NULL, 
`quantity` smallint(5) unsigned NOT NULL DEFAULT '1', 
PRIMARY KEY (`id_order`,`id_prod_ref`), 
INDEX (id_prod_ref, id_order) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Вот почему: od звучит как много: многие таблицы отображения. См. here для получения рекомендаций по повышению производительности в нем.

GROUP BYобычно включает в себя сортировку. ORDER BY, когда он не идентичен GROUP BY, определенно требуется другой вид.

Снятие ORDER BY позволяет запросить возврат любых 10 строк без сортировки. (Это может объяснить разницу во времени.)

Примечание псевдоним sum_quantity, чтобы избежать неоднозначности между колонкеquantity и ваш псевдонимquantity.

Разъяснение РАЗЪЯСНЯЕТ

1 SIMPLE o range id_order_status 1    75940 Using where; Using index; Using temporary; Using filesort 
1 SIMPLE od ref  id_order  4 o.id_order  1 
1 SIMPLE pr eq_ref PRIMARY   4 od.id_prod_ref 1 Using where 
1 SIMPLE p eq_ref PRIMARY   4 pr.id_prod  1 Using index 
  • Таблица будет доступна в указанном порядке (о, оды, пр, р).
  • o не будет использовать данные («Использование индекса»), но сканирует индекс id_order_status, который включает в себя (id_status, id_order). Примечание: столбцы PRIMARY KEY: неявно добавлено к любому второстепенному ключу.
  • Это оценки 76K необходимо будет отсканировать (для> 11).
  • Где-то в обработке будет временная таблица и ее вид. Это может или может не включать диск ввода-вывода.
  • Досягаемость в od может найти 1 ряд, может быть найден 0 или более 1 ("ref").
  • Известно, что достижение уровня pr и p составляет не более 1 строки.
  • pr делает небольшое количество фильтров (active=1), но не до третьей строки EXPLAIN. И никакой индекс не полезен для этой фильтрации. Это можно было бы улучшить, но лишь незначительно, по составному индексу (active, id_prod_ref). При отфильтровании всего 5-10% это не поможет.
  • В конце концов JOINing и фильтрация будет temp столы и сортировки, одна для GROUP BY, одна для ORDER BY.
  • Только после этого будет отслаиваться 10 строк из 70K (или около того) строк, собранных до этой точки.

Без заказа, EXPLAIN показывает, что другой порядок выглядит лучше. И сортировка tmp & исчезла.

1 SIMPLE p index PRIMARY  4      1 Using index 
1 SIMPLE pr ref  id_prod  4 p.id_prod   2 Using where 
1 SIMPLE od ref  id_prod_ref 4 pr.id_prod_ref 67 
1 SIMPLE o eq_ref PRIMARY  4 dbne.od.id_order 1 Using where 
  • Там, кажется, только 1 строка в p, правильно? Таким образом, это не имеет значения, когда эта таблица доступна. Когда у вас есть несколько «продуктов», весь этот анализ может измениться!
  • «key = PRIMARY», «Использование индекса» - это своего рода неправильное обозначение. Он действительно использует данные, но имеет возможность эффективно обращаться к нему, потому что PRIMARY KEY «сгруппирован» с данными.
  • Есть только один номер pr? Возможно, оптимизатор понял, что GROUP BY не нужен?
  • Когда он добрался до od, он подсчитал, что для каждой комбинации p + pr потребуется число «67».
  • Вы удалили ORDER BY, поэтому вам не нужно сортировать, и любой 10 строк могут быть доставлены.
+0

Привет, Рик, спасибо за ваш ответ. На самом деле, мне нужно присоединиться к таблицам продуктов и заказов, потому что я получаю некоторые значения из них! и даже с запросом, о котором вы указали, он все еще очень медленный, около 0,7 с: /, и если я удалю ORDER BY, 0,04. Я отредактирую свой первоначальный пост с помощью SHOW CREATE TABLE – Websphere

+0

Зная, какие поля вы получите, это имеет значение! –

+0

Я обновил свое сообщение со структурой таблиц. Надеюсь, теперь все ясно. – Websphere

Смежные вопросы