Intereting Posts
как однозначно идентифицировать удаленный компьютер в php? Искать блок текста, возвращать ссылки в формате MP3 с помощью PHP программа can not start, потому что php5.dll отсутствует Использование X-editable для создания нескольких полей, редактируемых в существующей горизонтальной форме bootstrap3 Удаление любого существующего файла из Dropzone показывает dictDefaultMessage функции, используемые для шифрования пароля в php? MySQL Triggers last_insert_id () Неустранимая ошибка PDO показывает имя пользователя и пароль mcrypt_encrypt не работает должным образом на PHP 5.6.9 PHP & cron: проблемы безопасности PHP-скрипт – определить, работает ли Linux или Windows? Лучший способ хранения сообщений и файлов чата Codeigniter: Сообщение: не удается изменить информацию заголовка – заголовки, уже отправленные (вывод запущен в передать переменную php в javascript Неустранимая ошибка: вызов неопределенной функции mcrypt_encrypt ()

MySQL «in-where» работает медленно?

У меня есть две таблицы: Posts и Tags , в которых хранятся статьи для публикации пользователей и теги, которые они прикрепляют к статьям. Таблица PostTags используется для обозначения отношения идентификаторов статей и идентификаторов тегов. Структуры представлены ниже:

Сообщения:

 id | title | author_id | create_time | update_time | ... #(title, author_id, create_time) is unique 

Метки:

 id | tag_text | create_time #tag_text is unique and index 

PostTags:

 id | post_id | tag_id #(post_id, tag_id) is unique 

Теперь я использую следующий sql для получения статей с соответствующими тегами (с помощью group_concat).

 SELECT p.id, p.title, t.tag AS Tags FROM Posts p LEFT JOIN Tags t on t.id IN (SELECT tag_id FROM PostTags WHERE post_id=s.id) GROUP BY p.id ORDER BY p.update_time DESC LIMIT 0, 10 

Но я нахожу это очень медленным (для 2.5k строк статей и 600 тегов, требуется> 3s). Как я могу улучшить производительность?

Результат EXPLAIN выглядит следующим образом:

 id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra 1 | PRIMARY | p | ALL | NULL | NULL | NULL | NULL | 2569 | Using temporary; Using filesort 1 | PRIMARY | t | ALL | NULL | NULL | NULL | NULL | 616 2 | DEPENDENT SUBQUERY | PostTags | index_subquery | unique_index,tag_id,post_id | tag_id | 4 | func | 1 | Using where 

PS, мой оригинальный sql (с group_concat)

 SELECT p.id, p.title, group_concat(DINSTINCT t.tag) AS Tags FROM Posts p LEFT JOIN Tags t on t.id IN (SELECT tag_id FROM PostTags WHERE post_id=s.id) GROUP BY p.id ORDER BY p.update_time DESC LIMIT 0, 10 

Но случай без group_concat такой же.

Документация MySQL описывает именно такую ​​ситуацию :

Типичный случай низкой производительности подзапроса IN – это когда подзапрос возвращает небольшое количество строк, но внешний запрос возвращает большое количество строк для сравнения с результатом подзапроса.

Проблема в том, что для оператора, который использует подзапрос IN, оптимизатор переписывает его как коррелированный подзапрос. [..] Если внутренний и внешний запросы возвращают строки M и N, соответственно, время выполнения становится порядка O (M × N), а не O (M + N), как это было бы для некоррелированного подзапроса .

Использование другого соединения вместо подзапроса было бы более оптимальным решением:

 SELECT p.id, p.title, t.tag AS Tags FROM Posts p LEFT JOIN PostTags pt on pt.post_id = p.id LEFT JOIN Tags t on t.id = pt.tag_id GROUP BY p.id ORDER BY p.update_time DESC LIMIT 0, 10 

Вот запрос с group_concat и group_concat имеет ничего общего со скоростью.

 select p.id, p.title, group_concat(t.tag_text) as post_tags from Post p left join PostTags pt on pt.post_id = p.id left join Tags t on pt.tag_id = t.id group by p.id order by p.udate_time desc limit 0,10 

У вас уже есть некоторые индексы в таблице, которые пока хороши, однако добавление другого индекса увеличит запрос

 alter table Posts add index updated_time_idx(updated_time);