@kirill-93

Почему union так сильно замедляет запрос?

Решил оптимизировать запрос.
Запрос вытаскивал посты из таблицы posts по некоторым условиями и сортировал их по дате или непрочитанности. Непрочитанность - это отсутствие постов в таблице post_user.
В итоге получался примерно такой код для сортировке по дате
select * from posts where ... order by created_at desc

и такой для сортировке по непрочитанности
select * from posts 
left join post_user on post_user.post_id = posts.id and post_user.user_id = 2
where ...
order by post_user.id desc, posts.created_at desc

Второй запрос работает намного медленнее первого. Поэтому я решил отдельно выбрать все непрочитанные новости и отдельно все прочитанные. Просто склеить 2 результата не получится, потому что в запросах используется limit offset, поэтому я решил объединить 2 запроса в один с помощью union.
Получилось так:
(select 0 as is_unread, * from posts where ...)
union
(select 1 as is_unread, * from posts where ... and not exists(select * from post_user where post_id = posts.id and user_id = 2))
order by is_unread desc, created_at desc
limit 20 offset 0


Оба этих запроса по отдельности выполняются достаточно быстро и в explain все хорошо.
Но с использованием union значение поля rows увеличивается с 900 (если выполнять каждый запрос отдельно) до 200000. Дело не в сортировке, если выполнить запрос без сортировки, ничего не изменится, и даже если второй запрос в union будет полностью соответствовать первому, тоже ничего не изменится.
Почему так происходит?
И как бы вы сделали сортировку в таком случае?
  • Вопрос задан
  • 1569 просмотров
Пригласить эксперта
Ответы на вопрос 2
Потому что union это псевдоним для "UNION DISTINCT", т.е. сначала создается временная таблица, потом перебираются все строки для удаления дублей и потом строки сортируются. Попробуйте заменить на "UNION ALL" разница в скорости выполнения будет в разы, но не уверен что для вашего случая такая замена корректна.
Ответ написан
Комментировать
@AlikDex
VQ5XP.png
если коротко, то одним запросом выбрать все явно не прочитанные. Т.е. выбираем только те, которые присутствуют во второй таблице через RIGTH JOIN ... ON A.Key=B.Key. А другим запросом используя юнион олл, и выбираем все явно прочитанные, через конструкцию RIGTH JOIN ... WHERE A.Key IS NULL, таким образом исключая дубли и два запроса останутся быстрыми
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы