Подтвердить что ты не робот

Как избежать нескольких запросов с помощью: включить в Rails?

Если я сделаю это

post = Post.find_by_id(post_id, :include => :comments)

выполняется два запроса (один для пост-данных и другой для комментариев для комментариев). Затем, когда я делаю post.comments, другой запрос не выполняется, поскольку данные уже кэшированы.

Есть ли способ сделать только один запрос и получить доступ к комментариям через почтовые службы?

4b9b3361

Ответ 1

Нет, нет. Это предполагаемое поведение :include, поскольку подход JOIN в конечном итоге оказывается неэффективным.

Например, рассмотрим следующий сценарий: модель Post имеет 3 поля, которые вам нужно выбрать, 2 поля для Comment, и этот конкретный пост содержит 100 комментариев. Rails может запускать один запрос JOIN по строкам:

SELECT post.id, post.title, post.author_id, comment.id, comment.body
FROM posts
INNER JOIN comments ON comment.post_id = post.id
WHERE post.id = 1

Это вернет следующую таблицу результатов:

 post.id | post.title | post.author_id | comment.id | comment.body
---------+------------+----------------+------------+--------------
       1 | Hello!     |              1 |          1 | First!
       1 | Hello!     |              1 |          2 | Second!
       1 | Hello!     |              1 |          3 | Third!
       1 | Hello!     |              1 |          4 | Fourth!
...96 more...

Вы уже видите проблему. Подход с одним запросом JOIN, хотя он возвращает нужные вам данные, возвращает его избыточно. Когда сервер базы данных отправляет результирующий набор в Rails, он будет отправлять идентификатор сообщения, заголовок и идентификатор автора 100 раз каждый. Предположим, что в Post было 10 полей, которые вас интересовали, 8 из которых были текстовыми блоками. Eww. Это много данных. Передача данных из базы данных в Rails делает работу с обеих сторон, как в цикле ЦП, так и в ОЗУ, поэтому минимизация этой передачи данных важна для того, чтобы приложение выполнялось быстрее и компактнее.

Утилиты Rails сбивают числа, и большинство приложений работают лучше при использовании нескольких запросов, которые только извлекают каждый бит данных один раз, а не один запрос, который может стать чрезвычайно избыточным.

Конечно, наступит время в каждой жизни разработчика, когда соединение необходимо для выполнения сложных условий, и это может быть достигнуто путем замены :include на :joins. Однако для отношений предварительной выборки подход Rails, принятый в :include, намного лучше для производительности.

Ответ 2

Если вы используете это поведение для загруженных ассоциаций, вы получите единственный (и эффективный) запрос.

Вот пример:

  • Скажите, что у вас есть следующая модель (где :user - внешняя ссылка):

    class Item < ActiveRecord::Base
      attr_accessible :name, :user_id
      belongs_to :user
    end
    
  • Затем выполнение этой функции ( note: часть where имеет решающее значение, так как она трюки Rails для создания этого единственного запроса):

    @items = Item.includes(:user).where("users.id IS NOT NULL").all
    

    приведет к одному SQL-запросу (синтаксис ниже относится к PostgreSQL):

    SELECT "items"."id" AS t0_r0, "items"."user_id" AS t0_r1, 
            "items"."name" AS t0_r2, "items"."created_at" AS t0_r3,
            "items"."updated_at" AS t0_r4, "users"."id" AS t1_r0, 
            "users"."email" AS t1_r1, "users"."created_at" AS t1_r4, 
            "users"."updated_at" AS t1_r5 
    FROM "measurements" 
    LEFT OUTER JOIN "users" ON "users"."id" = "items"."user_id" 
    WHERE (users.id IS NOT NULL)