Рельсы выбирают и включают
Кто-нибудь может это объяснить?
Project.includes([:user, :company])
Это выполняет 3 запроса, один для извлечения проектов, один для извлечения пользователей для этих проектов и один для извлечения компаний.
Project.select("name").includes([:user, :company])
Выполняет 3 запроса и полностью игнорирует бит выбора.
Project.select("user.name").includes([:user, :company])
Выполняет 1 запрос с правильными левыми соединениями. И все же полностью игнорирует выбор.
Мне кажется, что рельсы игнорируют select with includes. Хорошо, но почему, когда я помещаю связанную модель в select, она переключается с выдачи 3 запросов на выдачу 1 запроса?
Обратите внимание, что 1 запрос - это то, что я хочу, я просто не могу себе представить, что это правильный способ его получить или почему он работает, но я не уверен, как еще получить результаты в одном запросе (.joins кажется, использует только INNER JOIN, которого я действительно не хочу, и когда я вручную специфицирую условия соединения для присоединения к камню поиска, мы используем freaks, поскольку он пытается повторно добавить объединения с тем же именем).
Ответы
Ответ 1
Хорошо, вот, что я придумал...
.joins("LEFT JOIN companies companies2 ON companies2.id = projects.company_id LEFT JOIN project_types project_types2 ON project_types2.id = projects.project_type_id LEFT JOIN users users2 ON users2.id = projects.user_id") \
.select("six, fields, I, want")
Работает, боль в прикладе, но мне нужны только те данные, которые мне нужны в одном запросе. Единственная паршивая часть - я должен дать все псевдоним model2, так как мы используем meta_search, который, похоже, не может понять, что таблица уже соединена, когда вы указываете свои собственные условия соединения.
Ответ 2
У меня была та же проблема с select и includes.
Для интенсивной загрузки связанных моделей я использовал собственную область Rails 'preload' http://apidock.com/rails/ActiveRecord/QueryMethods/preload
Он обеспечивает надежную загрузку без пропуска "select" в цепочке областей.
Я нашел его здесь https://github.com/rails/rails/pull/2303#issuecomment-3889821
Надеюсь, этот совет будет полезен для кого-то, поскольку это было полезно для меня.
Ответ 3
Rails всегда игнорировал аргументы select
при использовании include
или includes
. Если вы хотите использовать свой аргумент select, используйте вместо него joins
.
У вас может возникнуть проблема с камнем запроса, о котором вы говорите, но вы также можете включить sql-фрагменты, используя метод joins.
Project.select("name").joins(['some sql fragement for users', 'left join companies c on c.id = projects.company_id'])
Я не знаю вашу схему, поэтому мне нужно будет угадать точные отношения, но это должно вас начать.
Ответ 4
Я мог бы вообще что-то пропустить, но select
и include
не являются частью ActiveRecord. Обычный способ сделать то, что вы пытаетесь сделать, выглядит следующим образом:
Project.find(:all, :select => "users.name", :include => [:user, :company], :joins => "LEFT JOIN users on projects.user_id = users.id")
Посмотрите на документацию api для получения дополнительных примеров. Иногда мне приходилось идти вручную и использовать find_by_sql
:
Project.find_by_sql("select users.name from projects left join users on projects.user_id = users.id")
Надеемся, это укажет вам в правильном направлении.
Ответ 5
Я хотел эту функциональность самостоятельно, поэтому, пожалуйста, используйте ее.
Включите этот метод в свой класс
#ACCEPTS args в строковом формате "ASSOCIATION_NAME: COLUMN_NAME-COLUMN_NAME"
def self.includes_with_select(*m)
association_arr = []
m.each do |part|
parts = part.split(':')
association = parts[0].to_sym
select_columns = parts[1].split('-')
association_macro = (self.reflect_on_association(association).macro)
association_arr << association.to_sym
class_name = self.reflect_on_association(association).class_name
self.send(association_macro, association, -> {select *select_columns}, class_name: "#{class_name.to_sym}")
end
self.includes(*association_arr)
end
И вы сможете позвонить так: Contract.includes_with_select ('user: id-name-status', 'confirm: confirm-id'), и он выберет указанные столбцы.