Рельсы: включить против: присоединяется



это скорее вопрос "почему все так работает", а не вопрос" я не знаю, как это сделать"...



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



Post.all(:include => :comments)


однако, когда вы смотрите на журналы, там соединения не происходит:



Post Load (3.7ms)   SELECT * FROM "posts"
Comment Load (0.2ms) SELECT "comments.*" FROM "comments"
WHERE ("comments".post_id IN (1,2,3,4))
ORDER BY created_at asc)


это и принимая ярлык, потому что он тянет все комментарии сразу, но это все еще не соединение (что, похоже, говорит вся документация). Единственный способ получить соединение-использовать :joins вместо :include:



Post.all(:joins => :comments)


и журналы показывают:



Post Load (6.0ms)  SELECT "posts".* FROM "posts" 
INNER JOIN "comments" ON "posts".id = "comments".post_id


Я что-то пропустила? У меня есть приложение с полудюжиной ассоциаций, и на одном экране я показываю данные из всех них. Похоже, что было бы лучше иметь один запрос join-ed вместо 6 человек. Я знаю, что с точки зрения производительности это не всегда лучше делать соединение, а не отдельные запросы (на самом деле, если вы собираетесь тратить время, похоже, что два отдельных запроса выше быстрее, чем соединение), но после всех документов, которые я читал, я удивлен видеть :include не работает, как рекламируется.



Может Быть, Рельсы и осознает проблему производительности и не присоединяется, за исключением некоторых случаев?

618   8  

8 ответов:

получается, что :include функциональность была изменена с Rails 2.1. Rails использовался для объединения во всех случаях, но по соображениям производительности он был изменен для использования нескольких запросов в некоторых обстоятельствах. этот блог Фабио Акита имеет некоторую хорошую информацию об изменении (см. раздел "оптимизация предвыборка").

.joins просто присоединяется к таблицам и приносит выбранные поля в ответ. если вы вызываете ассоциации в результате запроса joins, он снова запускает запросы к базе данных

:includes будет стремиться загрузить включенные ассоциации и добавить их в память. :includes загружает все атрибуты таблицы. Если вы вызываете ассоциации на результат запроса include, он не будет запускать никаких запросов

мой блоге имеет некоторые подробные объяснения различий

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

например, если у вас есть таблица, полная комментариев, и вы используете :joins => users, чтобы вытащить всю информацию о пользователе для целей сортировки и т. д. Это будет работать нормально и займет меньше времени, чем :include, но скажите, что вы хотите отобразить комментарий вместе с именем пользователя, электронной почтой и т. д. Чтобы получить информацию используя: joins, он должен будет сделать отдельные SQL-запросы для каждого пользователя, которого он выбирает, тогда как если вы использовали :include эта информация готова к использованию.

пример:

http://railscasts.com/episodes/181-include-vs-joins

в дополнение к соображениям производительности, есть функциональная разница тоже. Когда вы присоединяетесь к комментариям, вы просите сообщения, которые имеют комментарии - внутреннее соединение по умолчанию. Когда вы включаете комментарии, вы просите все сообщения - внешнее соединение.

Я недавно читал больше о разнице между :joins и :includes в рельсы. Вот объяснение того, что я понял (с примерами :))

рассмотрим такой сценарий:

  • пользователь has_many комментарии и комментарий принадлежит пользователю.

  • модель пользователя имеет следующие атрибуты: имя(строка), возраст(целое число). Модель комментариев имеет следующие атрибуты: Content, user_id. За комментарием есть функция user_id может быть ноль.

присоединяется:

: joins выполняет a внутреннее соединение между двумя таблицами. Таким образом

Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>

получать все записи, где user_id (таблицы комментариев) равен user.id (таблица пользователей). таким образом, если вы делаете

Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>

вы получите пустой массив, как показано.

кроме того, joins не загружает объединенную таблицу в память. Таким образом, если вы делаете

comment_1 = Comment.joins(:user).first

comment_1.user.age
#=>←[1m←[36mUser Load (0.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1←[0m  [["id", 1]]
#=> 24

Как видите, comment_1.user.age снова вызовет запрос базы данных в фоновом режиме, чтобы получить результаты

включает в себя:

: включает в себя выполняет левое внешнее соединение между двумя таблицами. Таким образом

Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

в результате объединенная таблица со всеми записями из таблицы комментариев. таким образом, если вы делаете

Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

он будет получать записи, где комментарии.user_id равен нулю как показанный.

кроме того, включает в себя загрузку обеих таблиц в памяти. Таким образом, если вы делаете

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

как вы можете заметить comment_1.пользователь.возраст просто загружает результат из памяти без запуска запроса базы данных в фоновом режиме.

tl; dr

я сравниваю их двумя способами:

соединения - для условного выбора записей.

включает в себя - при использовании ассоциации для каждого члена результирующего набора.

версия

соединения предназначены для фильтрации результирующего набора, поступающего из базы данных. Вы используете его, чтобы сделать набор операций на вашем столе. Подумайте об этом как о предложении where, которое выполняет set теория.

Post.joins(:comments)

это то же самое, что

Post.where('id in (select post_id from comments)')

за исключением того, что если есть более одного комментария, вы получите дубликаты сообщений с объединениями. Но каждый пост будет пост, что комментарии. Вы можете исправить это с помощью distinct:

Post.joins(:comments).count
=> 10
Post.joins(:comments).distinct.count
=> 2

в договоре includes метод будет просто убедиться, что нет никаких дополнительных запросов к базе данных при ссылке на отношение (так что мы не делаем n + 1 запросы)

Post.includes(:comments).count
=> 4 # includes posts without comments so the count might be higher.

мораль, использовать joins если вы хотите выполнять операции условного набора и использовать includes когда вы собираетесь использовать связь для каждого члена коллекции.

.joins работает как соединение с базой данных, и он соединяет две или более таблицы и извлекает выбранные данные из бэкэнда(базы данных).

.включает работу как левое соединение базы данных. Он загрузил все записи левой стороны, не имеет отношения к правой стороне модели. Он используется для нетерпеливой загрузки, потому что он загружает все связанные объекты в памяти. Если мы вызываем ассоциации на результат запроса include, то он не запускает запрос в базе данных, а просто возвращает данные из памяти, потому что они уже есть загруженные данные в память.

'joins' просто используется для объединения таблиц, и когда вы вызываете ассоциации на соединениях, то он снова запускает запрос (это означает, что многие запросы будут срабатывать)

lets suppose you have tow model, User and Organisation
User has_many organisations
suppose you have 10 organisation for a user 
@records= User.joins(:organisations).where("organisations.user_id = 1")
QUERY will be 
 select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1

it will return all records of organisation related to user
and @records.map{|u|u.organisation.name}
it run QUERY like 
select * from organisations where organisations.id = x then time(hwo many organisation you have)

общее количество SQL составляет 11 в этом случае

но с 'includes' будет охотно загружать включенные ассоциации и добавлять их в память(загружать все ассоциации при первой загрузке), а не запускать запрос снова

когда вы получаете записи включает в себя как @учетная документация= Пользователь.включает (организации).где ("организации.функция user_id = 1") тогда запрос будет

select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1
and 


 select * from organisations where organisations.id IN(IDS of organisation(1, to 10)) if 10 organisation
and when you run this 

@records. map{|u / u.organisation. name} ни один запрос не будет срабатывать

Comments

    Ничего не найдено.