Rails 3: запрос ActiveRecord с: include возвращает только результаты, имеющие связанные значения во включенной таблице? - PullRequest
3 голосов
/ 19 августа 2011

У меня есть модель (Feature), которая может иметь много Assets. Эти Assets каждый имеет issue_date. Я борюсь с тем, что кажется простым запросом ActiveRecord, чтобы найти все Features и их Assets с issue_date завтрашнего дня, независимо от того, есть Assets или нет - предпочтительно с один запрос.

Вот мой запрос прямо сейчас.

Feature.includes(:assets).where(:assets => { :issue_date => Date.tomorrow })

К сожалению, это возвращает только Features, которые имеют Assets с issue_date завтрашнего дня. Еще более странно, что сгенерированный SQL выглядит так (завтра, очевидно, 19-е).

SELECT `features`.* FROM `features`  WHERE `assets`.`issue_date` = '2011-08-19'

Разве это не должно иметь где-то там LEFT JOIN? Это то, что я собираюсь сделать. Использование joins вместо includes дает INNER JOIN, но это не то, что я хочу. Как ни странно, похоже, что у меня INNER JOIN -тип поведения. Когда я запускаю этот запрос includes, приведенный выше, выкладываемый SQL выглядит примерно так ...

SELECT `features`.`id` AS t0_r0, `features`.`property_id` AS t0_r1,
    // every other column from features truncated for sanity
    `assets`.`feature_id` AS t1_r1, `assets`.`asset_type` AS t1_r2,
    // all other asset columns truncated for sanity
FROM `features`
LEFT OUTER JOIN `assets` ON `assets`.`feature_id` = `features`.`id`
WHERE `assets`.`issue_date` = '2011-08-19'

Который выглядит как будто он должен работать правильно, но это не так. Я получаю только Features, которые имеют Assets с issue_date завтрашнего дня. Есть идеи, что я делаю не так?

Я попробовал более старый способ Rails v2…

Feature.find(:all,
             :include => :assets,
             :conditions => ['assets.issue_date = ?', Date.tomorrow])

Что дает мне такие же результаты. Есть один Feature Я знаю, что не имеет Assets на завтра, и его нет в этом списке.

Я также ковырялся и находил похожие вопросы, но я не мог найти тот, который объяснил бы противоположное поведение, которое я вижу.

Редактировать : Я , поэтому близко. Это дает мне все Feature объекты.

Feature.joins("LEFT OUTER JOIN assets on assets.feature_id = feature.id AND asset.issue_date = #{Date.tomorrow}")

Это не , однако, получите мне соответствующий Assets, связанный в объект. С feature в качестве возвращаемого элемента в запросе feature.assets делает еще один вызов в базу данных, что мне не нужно. Я хочу, чтобы feature.assets возвращал только те, которые я указал в этом LEFT OUTER JOIN вызове. Что еще мне нужно сделать с моим запросом?

Ответы [ 4 ]

3 голосов
/ 19 августа 2011

Я думал, что это даст мне то, что мне нужно, но это не так.Вызов feature.assetsfeature в качестве элемента, возвращенного в моем запросе) выполняет другой запрос для поиска всех assets, связанных с этим feature.

Feature.joins("LEFT OUTER JOIN assets on assets.feature_id = feature.id AND asset.issue_date = #{Date.tomorrow}")

Так вот что делаетРабота.Кажется, немного чище. На моей Feature модели уже установлен has_many :assets.Я установил другую ассоциацию с has_many :tomorrows_assets, которая указывает на Assets, но с условием для этого.Затем, когда я запрашиваю Feature.all или Feature.name_of_scope, я могу указать .includes(:tomorrows_assets).Победитель, куриный ужин.

has_many :tomorrows_assets,
  :class_name => "Asset",
  :readonly => true, 
  :conditions => "issue_date = '#{Date.tomorrow.to_s}'"

Я могу успешно запросить Features и получить только то, что мне нужно, с ним, только если это соответствует заданным критериям (и я установил :readonly, потому что язнаю, я никогда не захочу редактировать Assets, как это).Вот сеанс IRB, который показывает магию.

features = Feature.includes(:tomorrows_assets)
feature1 = features.find_all{ |f| f.name == 'This Feature Has Assets' }.first
feature1.tomorrows_assets
=> [#<Asset id:1>, #<Asset id:2>]

feature2 = features.find_all{ |f| f.name == 'This One Does Not' }.first
feature2.tomorrows_assets
=> []

И всего лишь два SQL-запроса.

2 голосов
/ 06 марта 2013

У меня была очень похожая проблема, и мне удалось решить ее с помощью следующего запроса;

Feature.includes(:assets).where('asset.id IS NULL OR asset.issue_date = ?', Date.tomorrow)

Это загрузит все функции, независимо от того, есть ли у них какие-либо активы. Вызов feature.asset вернет массив ресурсов, если он доступен, без выполнения другого запроса

Надеюсь, это кому-нибудь поможет!

1 голос
/ 19 августа 2011

Вы должны сами указать SQL для внешних объединений, метод joins использует только внутренние объединения.

Feature.joins("LEFT OUTER JOIN assets ON assets.feature_id = features.id").
where(:assets => {:issue_date => Date.tomorrow})
0 голосов
/ 19 августа 2011

Вы пробовали:

Feature.joins( :assets ).where( :issue_date => Date.tomorrow );

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

http://guides.rubyonrails.org/active_record_querying.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...