Как избежать множественных запросов с помощью: include в Rails? - PullRequest
12 голосов
/ 06 июня 2011

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

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

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

Есть ли способ сделать только один запрос и при этом получить доступ к комментариям через post.comments?

Ответы [ 2 ]

32 голосов
/ 06 июня 2011

Нет, нет.Это предполагаемое поведение :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, намного лучше для производительности.

5 голосов
/ 05 февраля 2013

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

Вот пример:

  • Допустим, у вас есть следующая модель (где :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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...