Медленный запрос для таблиц соединения - PullRequest
1 голос
/ 21 мая 2019

Я пытаюсь оптимизировать следующий запрос:

Существует две таблицы: Post (> 1 миллион записей) и Category (около 10-20 записей).

Запрос должен получить все posts, у которых category_id равен 1 и 2. Для таблиц соединения posts_categories имеется индекс для post_id и category_id, а оператор объяснения подтверждает, что Индекс используется в запросе.

Однако запрос все еще медленный. Похоже, что причиной здесь является группировка.

Ценю некоторые советы по его улучшению.

Post
   .joins(:categories)
   .where(categories: { id: [1,2] })
   .group(:post_id) 
   .having("COUNT(categories.id) = 2")
 SELECT  `posts`.* FROM `posts` 
   INNER JOIN `posts_categories` ON `posts_categories`.`post_id` = `posts`.`id` 
   INNER JOIN `categories` ON `categories`.`id` = `posts_categories`.`category_id` 
   WHERE `categories`.`id` IN (1, 2) 
   GROUP BY `post_id` 
   HAVING (COUNT(categories.id)=2)

Ответы [ 2 ]

3 голосов
/ 21 мая 2019

Это может не сработать, но стоит попробовать.

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

Вам нужно написать простую модель ActiveRecord в вашей таблице соединений PostsCategories ...

Post
  .where(id: PostCategory.where(category_id: [1,2]).select(:post_id))
  .joins(:post_categories)
  .group(:post_id) 
  .having("COUNT(post_category.category_id)=2")

Итак, преимущества здесь:

  • Вы больше не присоединяетесь к большой таблице категорий через таблицу соединения.
  • Подзапрос позволяет базе данных выбирать только PostCategories с этими идентификаторами категорий, сужая это поле.
0 голосов
/ 21 мая 2019

Извините, еще одна вещь, которую стоит попробовать.

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

Post.find_by_sql(
  "
    select * from posts where id in (
      SELECT cp1.post_id FROM JOIN categories_posts cp1
      INNER JOIN categories_posts cp2 ON cp1.post_id = cp2.post_id
      where cp1.category_id = 1 and cp2.category_id = 2
    )
  "
)

Другая вещь, которую следует учитывать, это подкачка страниц, вы можете попробовать добавить что-то вроде этого, чтобы посмотреть только раздел ваших сообщений:

and cp1.post_id between 1 and 10000

(проверено наOracle, могут быть некоторые синтаксические различия)

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