1

includes積極的な読み込みを維持しながら、アクティブなレコードで生成された ON 句に条件を追加するにはどうすればよいですか?

これらのクラスがあるとしましょう:

class Car
   has_many :inspections
end

class Inspection
   belongs_to :car
end

今私はできる:

Car.includes(:inspections)

Select * from cars LEFT OUTER JOIN inspections ON cars.id = inspections.car_id

しかし、私はこのSQLを生成したい:

Select * from cars LEFT OUTER JOIN inspections ON cars.id = inspections.car_id 
    AND inspections.month = '2013-04-01'

(これは機能しません):

Car.includes(:inspections).where("inspections.month = 2013-04-01")

Select * from cars LEFT OUTER JOIN inspections ON cars.id = inspections.car_id
    WHERE inspections.month = '2013-04-01'
4

4 に答える 4

1

私はこれを正確に知りませんが、あなたがやろうとしていることはおそらく推奨されていません。つまり、Rails の規則の 1 つに違反しています。関連する質問のこの回答によると、このようなクエリのデフォルトの動作は、次のような 2 つのクエリを使用することです。

SELECT "cars".* FROM "cars";
SELECT "inspections".* FROM "inspections" WHERE "inspections"."car_id" IN (1, 2, 3, 4, 5);

この決定は、パフォーマンス上の理由から行われました。これは、クエリの正確なタイプ (JOIN または複数のクエリ) は、信頼できない実装の詳細であると推測します。この一連の考えに沿って進むと、ActiveRecord::Relation はおそらくあなたのユース ケース向けに設計されたものではなく、おそらくONクエリに条件を追加する方法はありません。

この一連の推測に沿って、ユース ケースがユニークであると本当に信じている場合は、次のように独自の SQL クエリを作成することをお勧めします。

Car.joins(sanitize_sql_array(["LEFT OUTER JOIN inspections ON inspections.car_id = cars.id AND inspections.month = ?", "2013-04-01"])

(更新: これは昨年尋ねられたもので、適切な回答が得られませんでした。)

代替案 1

カルロス・ドリューが示唆したように、

@cars   = Cars.all
car_ids = @cars.map(&:id) 
@inspections = Inspection.where(inspections: {month: '2013-04-01', car_id: car_ids})
# with scopes: Inspection.for_month('2013-04-01').where(car_id: car_ids)

ただし、car.inspections不要な SQL 呼び出しをトリガーしないようにするために、次のことも行う必要があります。

# app/models/car.rb
has_many :inspections, inverse_of: :car

# app/models/inspection.rb
belongs_to :car, inverse_of: :inspections

代替案 2

おそらく、当月の検査をキャッシュする方法を見つけることができます。そうすれば、熱心な読み込みについて心配する必要はありません。キャッシュはさまざまな場所で再利用できるため、これが最適なソリューションである可能性があります。

@cars = Cars.all
@cars.each do |car|
  car.inspections.where(month: '2013-04-01')
end
于 2013-07-19T03:52:57.913 に答える
0

Rails の作成者は、この機能を ActiveRecord に組み込みませんでした。おそらく、WHERE を使用しても同じ結果セットが返され、別の方法を用意する必要がないと感じたためです。

ドキュメントとコードには、含まれるモデルに条件を追加する「公式」の 2 つの方法があります。

実際のソース コード: https://github.com/rails/rails/blob/5245648812733d2c31f251de3e05e78e68bfa3a5/activerecord/lib/active_record/relation/query_methods.rbでは、 WHERE を使用してこれを実現しています。

そして私は引用します: "

===条件

#
# If you want to add conditions to your included models you'll have
# to explicitly reference them. For example:
#
# User.includes(:posts).where('posts.name = ?', 'example')
#
# Will throw an error, but this will work:
#
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)

_END_QUOTE_

ドキュメントでは、別のアプローチについて言及しています: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.htmlヘッダーの下の「Eager loading of associations」

見積もり:

アソシエーションの一部のメンバーのみを積極的に読み込みたい場合は、通常、条件が定義されているアソシエーションを含める方が自然です。

class Post < ActiveRecord::Base has_many :approved_comments, -> { where approval: true }, class_name: 'Comment' end

Post.includes(:approved_comments)

これにより、投稿が読み込まれ、承認されたコメントのみが含まれるapproved_commentsアソシエーションが熱心に読み込まれます。

見積もりを終了

技術的にはこのようなアプローチを使用できますが、動的な月の値を使用している場合はあまり役に立たない場合があります。

これらは唯一のオプションであり、いずれの場合も AND ベースのクエリと同じ結果を返します。

于 2013-07-19T03:49:46.383 に答える
0

これはうまくいくはずです:

Car.includes(:inspections).where( inspections: { month: '2013-04-01' })
于 2013-03-28T20:41:15.550 に答える