MySQL: как ускорить запрос «Count ()» с помощью «JOIN» и «order_by»? - PullRequest
1 голос
/ 21 августа 2011

В моей базе данных MySQL есть следующие две (упрощенные для примера) таблицы:

DESCRIBE appname_item;

-----------------+---------------+------+-----+---------+----------------+
| Field           | Type          | Null | Key | Default | Extra          |
+-----------------+---------------+------+-----+---------+----------------+
| id              | int(11)       | NO   | PRI | NULL    | auto_increment |
| name            | varchar(200)  | NO   |     | NULL    |                |
+-----------------+---------------+------+-----+---------+----------------+

DESCRIBE appname_favorite;

+---------------+----------+------+-----+---------+----------------+
| Field         | Type     | Null | Key | Default | Extra          |
+---------------+----------+------+-----+---------+----------------+
| id            | int(11)  | NO   | PRI | NULL    | auto_increment |
| user_id       | int(11)  | NO   | MUL | NULL    |                |
| item_id       | int(11)  | NO   | MUL | NULL    |                |
+---------------+----------+------+-----+---------+----------------+

Я пытаюсь получить список элементов, упорядоченных по количеству избранных.Приведенный ниже запрос работает, однако в таблице Item есть тысячи записей, и на его выполнение может уйти до нескольких минут.

SELECT `appname_item`.`id`, `appname_item`.`name`, COUNT(`appname_favorite`.`id`) AS `num_favorites` 
FROM `appname_item` 
LEFT OUTER JOIN `appname_favorite` ON (`appname_item`.`id` = `appname_favorite`.`item_id`) 
GROUP BY `appname_item`.`id`, `appname_item`.`name` 
ORDER BY `num_favorites` DESC;

Вот результаты EXPLAIN, которые дают некоторое представлениечто касается того, почему запрос такой медленный (введите «ALL», «используя временный» и «используя файловую сортировку», следует по возможности избегать всех).

+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
| id | select_type | table              | type | possible_keys               | key                         | key_len | ref                           | rows | Extra                           |
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
|  1 | SIMPLE      | appname_item       | ALL  | NULL                        | NULL                        | NULL    | NULL                          |  574 | Using temporary; Using filesort |
|  1 | SIMPLE      | appname_favorite   | ref  | appname_favorite_67b70d25   | appname_favorite_67b70d25   | 4       | appname.appname_item.id       |    1 |                                 |
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+

Я знаю, что самый простой способ оптимизироватьЗапрос состоит в добавлении индекса, но я не могу понять, как добавить индекс для запроса Count (), который включает JOIN и order_by.Я должен также упомянуть, что я выполняю это через Django ORM, поэтому предпочел бы не изменять SQL-запрос, а просто работать над исправлением и точной настройкой БД для наиболее эффективного выполнения запроса.

IЯ пытался выяснить это некоторое время, поэтому любая помощь будет высоко ценится!

ОБНОВЛЕНИЕ

Вот индексы, которые уже находятся в БД:

+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table              | Non_unique | Key_name                    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| appname_favorite   |          0 | PRIMARY                     |            1 | id          | A         |         594 |     NULL | NULL   |      | BTREE      |         |
| appname_favorite   |          1 | appname_favorite_fbfc09f1   |            1 | user_id     | A         |          12 |     NULL | NULL   |      | BTREE      |         |
| appname_favorite   |          1 | appname_favorite_67b70d25   |            1 | item_id     | A         |         594 |     NULL | NULL   |      | BTREE      |         |
+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

Ответы [ 3 ]

2 голосов
/ 21 августа 2011

На самом деле вы не можете избежать сортировки файлов, потому что число определяется во время расчета и неизвестно в индексе.Единственное решение, которое я могу себе представить, - это создать составной индекс для таблицы appname_item , который может немного помочь или нет, в зависимости от ваших конкретных данных:

ALTER TABLE appname_item ADD UNIQUE INDEX `item_id_name` (`id` ASC, `name` ASC);
1 голос
/ 21 августа 2011

В вашем запросе нет ничего плохого - он выглядит хорошо.

Возможно, оптимизатор имеет устаревшую информацию о таблице.Попробуйте выполнить это:

ANALYZE TABLE <tableaname>;

для всех задействованных таблиц.

0 голосов
/ 24 августа 2013

Во-первых, для функции count () вы можете проверить этот ответ, чтобы узнать более подробно: https://stackoverflow.com/a/2710630/1020600

Например, при использовании MySQL count (*) будет быстрым в таблице MyISAM но медленно под InnoDB. В InnoDB вы должны использовать count (1) или кол-(рк)

Если ваши механизмы хранения - MYISAM и если вы хотите рассчитывать на строку (я так думаю), достаточно использовать count (*).

Из вашего ОБЪЯСНЕНИЯ я обнаружил, что нет ключа для appname_item, если я попытаюсь добавить условие

where `appname_item`.`id` = `appname_favorite`.`item_id`

затем появляется «ключ». так смешно, но это работа.

Окончательный sql, как это

explain SELECT `appname_item`.`id`, `appname_item`.`name`, COUNT(*) AS `num_favorites` 
FROM `appname_item` 
LEFT OUTER JOIN `appname_favorite` ON (`appname_item`.`id` = `appname_favorite`.`item_id`) 
where `appname_item`.`id` = `appname_favorite`.`item_id`
GROUP BY `appname_item`.`id`, `appname_item`.`name` 
ORDER BY `num_favorites` DESC;

+ ---- + ------------- + ------------------ + -------- + --------------- + --------- + --------- + ------------- ------------------ + ------ + ------------------------ ---------------------- + | id | select_type | стол | тип | возможные_ключи | ключ
| key_len | ref | строки | Extra
| + ---- + ------------- + ------------------ + -------- + - ------------- + --------- + --------- + ---------------- --------------- + ------ + --------------------------- ------------------- + | 1 | ПРОСТО | appname_favorite | индекс | item_id | item_id | 5 | NULL | 2312 | С помощью индекс; Используя временные; Использование сортировки файлов | | 1 | ПРОСТО | appname_item | eq_ref | ПЕРВИЧНЫЙ | ПЕРВИЧНЫЙ | 4 | test.appname_favorite.item_id | 1 | Используя где
| + ---- + ------------- + ------------------ + -------- + - ------------- + --------- + --------- + ---------------- --------------- + ------ + --------------------------- ------------------- +

На моем компьютере таблица appname_item имеет 1686 строк, а appname_favorite имеет 2312 строк, старый sql занимает от 15 до 23 мс. Новый sql занимает от 3,7 до 5,3 мс

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...