Это скорее вопрос «почему все работает так», а не вопрос «я не знаю, как это сделать» ...
Таким образом, Евангелие при извлечении связанных записей, которое, как вы знаете, вы собираетесь использовать, заключается в том, чтобы использовать его, :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
Я что-то упускаю? У меня есть приложение с полдюжиной ассоциаций, и на одном экране я отображаю данные по всем из них. Похоже, было бы лучше иметь один объединенный запрос вместо 6 отдельных. Я знаю, что с точки зрения производительности не всегда лучше выполнять объединение, а не отдельные запросы (на самом деле, если вы тратите время, похоже, что два вышеупомянутых отдельных запроса выполняются быстрее, чем объединение), но после всех документов Я читал, я удивлен, увидев :include
не работает, как рекламируется.
Может быть , Rails является сознают проблемы производительности и не присоединяется , за исключением некоторых случаев?
источник
includes
(для тех, кто читает это)Ответы:
Похоже, что
:include
функциональность была изменена с Rails 2.1. Rails использовался для объединения во всех случаях, но по соображениям производительности в некоторых случаях было изменено использование нескольких запросов. Этот пост в блоге Фабио Акиты содержит полезную информацию об изменениях (см. Раздел «Оптимизированная загрузка с нетерпением»).источник
.joins
просто присоединяется к таблицам и возвращает выбранные поля. если вы вызовете ассоциации для результата запроса объединений, он снова запустит запросы к базе:includes
будет стремиться загрузить включенные ассоциации и добавить их в память.:includes
загружает все включенные атрибуты таблиц. Если вы вызываете ассоциации на результат запроса включения, он не будет запускать какие-либо запросыисточник
Разница между объединениями и включением заключается в том, что использование оператора включения генерирует значительно больший запрос SQL, загружая в память все атрибуты из других таблиц.
Например, если у вас есть таблица, заполненная комментариями, и вы используете: joins => users для извлечения всей информации о пользователях для целей сортировки и т. Д., То она будет работать нормально и займет меньше времени, чем: include, но, скажем, вы хотите отобразить комментарий вместе с именем пользователя, адресом электронной почты и т. д. Чтобы получить информацию с помощью: объединений, он должен будет выполнять отдельные запросы SQL для каждого пользователя, которого выбирает, тогда как если вы использовали: include, эта информация готова к использованию.
Отличный пример:
http://railscasts.com/episodes/181-include-vs-joins
источник
Недавно я читал больше о разнице между
:joins
и:includes
в рельсах. Вот объяснение того, что я понял (с примерами :))Рассмотрим этот сценарий:
Пользователь has_many комментарии и комментарий принадлежит_ к пользователю.
Модель User имеет следующие атрибуты: Имя (строка), Возраст (целое число). Модель Comment имеет следующие атрибуты: Content, user_id. Для комментария user_id может быть нулевым.
Соединения:
: joins выполняет внутреннее соединение двух таблиц. таким образом
извлечет все записи, где user_id (из таблицы комментариев) равен user.id (таблица пользователей). Таким образом, если вы делаете
Вы получите пустой массив, как показано на рисунке.
Более того, объединения не загружают объединенную таблицу в память. Таким образом, если вы делаете
Как вы видите,
comment_1.user.age
запрос базы данных снова будет запущен в фоновом режиме, чтобы получить результатыВключает:
: включает выполнение левого внешнего соединения между двумя таблицами. таким образом
приведет к объединенной таблице со всеми записями из таблицы комментариев. Таким образом, если вы делаете
он будет получать записи, где comments.user_id равен nil, как показано.
Причем включает в себя загружает обе таблицы в память. Таким образом, если вы делаете
Как вы можете заметить, comment_1.user.age просто загружает результат из памяти, не отправляя запрос к базе данных в фоновом режиме.
источник
В дополнение к соображениям производительности, есть и функциональная разница. Когда вы присоединяетесь к комментариям, вы запрашиваете посты с комментариями - внутреннее объединение по умолчанию. Когда вы включаете комментарии, вы запрашиваете все сообщения - внешнее объединение.
источник
ТЛ; др
Я противопоставляю их двумя способами:
присоединяется - для условного отбора записей.
includes - При использовании ассоциации для каждого члена набора результатов.
Более длинная версия
Объединения предназначены для фильтрации набора результатов, поступающих из базы данных. Вы используете его для выполнения операций над таблицами. Думайте об этом как о предложении where, которое выполняет теорию множеств.
Post.joins(:comments)
такой же как
Post.where('id in (select post_id from comments)')
За исключением того, что если есть более одного комментария, вы получите повторяющиеся посты обратно с объединениями. Но каждый пост будет постом с комментариями. Вы можете исправить это с помощью:
В контракте
includes
метод просто гарантирует, что при обращении к связи нет дополнительных запросов к базе данных (чтобы мы не делали n + 1 запросов)Мораль такова: используйте,
joins
когда вы хотите выполнить операции условного набора, и используйте,includes
когда вы собираетесь использовать отношение для каждого члена коллекции.источник
distinct
получает меня каждый раз. Спасибо!.joins работает как объединение базы данных и объединяет две или более таблицы и извлекает выбранные данные из серверной части (базы данных).
Включает работу в качестве левого соединения базы данных. Загружены все записи левой стороны, не имеет значения правосторонняя модель. Он используется для быстрой загрузки, поскольку он загружает все связанные объекты в память. Если мы вызываем ассоциации в результате запроса на включение, то он не запускает запрос к базе данных, он просто возвращает данные из памяти, потому что он уже загрузил данные в память.
источник
'объединения' просто используются для объединения таблиц, и когда вы вызываете ассоциации для объединений, он снова запускает запрос (это означает, что многие запросы будут запускаться)
общее количество SQL в этом случае 11
Но с помощью «include» будет стремиться загрузить включенные ассоциации и добавить их в память (загрузить все ассоциации при первой загрузке) и не запускать запрос снова
когда вы получаете записи с помощью таких включений, как @ records = User.include (: organization) .where ("organisations.user_id = 1"), тогда запрос будет
@ records.map {| u | u.organisation.name} ни один запрос не будет запущен
источник