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

Рельсы выбирают и включают

Кто-нибудь может это объяснить?

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, поскольку он пытается повторно добавить объединения с тем же именем).

4b9b3361

Ответ 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'), и он выберет указанные столбцы.