2
@integration = Integration.first(:conditions=> {:integration_name => params[:integration_name]}, :joins => :broker, :select => ['`integrations`.*, `brokers`.*'])
$stderr.puts @integration.broker.id # This line causes Brokers to be queried again

結果:

Integration Load (0.4ms)   SELECT `integrations`.*, `brokers`.* FROM `integrations` INNER JOIN `brokers` ON `brokers`.id = `integrations`.broker_id WHERE (`integrations`.`integration_name` = 'chicke') LIMIT 1
Integration Columns (1.5ms)   SHOW FIELDS FROM `integrations`
Broker Columns (1.6ms)   SHOW FIELDS FROM `brokers`
Broker Load (0.3ms)   SELECT * FROM `brokers` WHERE (`brokers`.`id` = 1) 

brokersRailsがすでに参加/選択しているのに、Railsが再びデータベースにヒットする理由はありますか?

モデルは次のとおりです(ブローカー->統合は1対多の関係です)。これは不完全であり、それらの関係を確立する行のみを含めたことに注意してください

class Broker < ActiveRecord::Base

  # ActiveRecord Associations
  has_many :integrations

class Integration < ActiveRecord::Base

  belongs_to :broker

Rails / ActiveRecord 2.3.14を使用しているので、それを覚えておいてください。

私がIntegration.first(:conditions=> {:integration_name => params[:integration_name]}, :include => :broker)その行を行うと、2つSELECT

Integration Load (0.6ms)   SELECT * FROM `integrations` WHERE (`integrations`.`integration_name` = 'chicke') LIMIT 1
  Integration Columns (2.4ms)   SHOW FIELDS FROM `integrations`
  Broker Columns (1.9ms)   SHOW FIELDS FROM `brokers`
  Broker Load (0.3ms)   SELECT * FROM `brokers` WHERE (`brokers`.`id` = 1) 
4

2 に答える 2

8

オブジェクトのリロードを避けるためincludeに代わりに使用します。joinsBroker

Integration.first(:conditions=>{:integration_name => params[:integration_name]}, 
  :include => :broker)

テーブルの列を正規化しようとしていないため、select句を指定する必要はありません。brokers

注 1:

依存関係を熱心にロードしている間、AR は依存関係ごとに 1 つの SQL を実行します。あなたの場合、AR は main sql + brokersql を実行します。1行を取得しようとしているため、あまり効果がありません。N 行にアクセスしようとしている場合、依存関係を熱心にロードすると、N+1 問題を回避できます。

注 2:

場合によっては、カスタムの熱心な読み込み戦略を使用することが有益な場合があります。統合に関連するブローカー名を取得したいだけだと仮定しましょう。次のようにSQLを最適化できます。

integration = Integration.first(
  :select => "integrations.*, brokers.name broker_name",
  :conditions=>{:integration_name => params[:integration_name]}, 
  :joins => :broker)

integration.broker_name # prints the broker name

selectクエリによって返されるオブジェクトには、句内のエイリアス化されたすべての列が含まれます。

Integration対応するオブジェクトがなくてもオブジェクトを返したい場合、上記のソリューションは機能しませんBroker。を使用する必要がありますOUTER JOIN

integration = Integration.first(
  :select => "integrations.*, brokers.name broker_name",
  :conditions=>{:integration_name => params[:integration_name]}, 
  :joins => "LEFT OUTER JOIN brokers ON brokers.integration_id = integrations.id")
于 2012-06-22T01:40:24.003 に答える
2

オプションは:joins、アクティブなレコードに結合句をクエリに追加するだけです。実際には、返された行でアクティブなレコードに何もさせません。関連付けが読み込まれていないため、関連付けにアクセスするとクエリがトリガーされます

この:includeオプションは、関連付けを事前にロードすることに関するものです。アクティブ レコードには、これを行うための 2 つの戦略があります。1 つは大きな結合クエリを使用する方法で、もう 1 つは関連付けごとに 1 つのクエリを実行する方法です。デフォルトは後者であるため、2 つのクエリが表示されます。

Integration.preload(:broker)Rails 3.x では、またはを実行して、必要な戦略を決定できますIntegration.eager_graph(:broker)

Rails 2.x にはそのような機能がないため、できることは、戦略を決定するために使用されるヒューリスティックをだますことだけです。Rails は、order 節、select 節、または条件が含まれる関連付けの列を参照していると判断するたびに、joins 戦略に切り替えます (その場合に機能するのは joins 戦略だけであるため)。

たとえば、次のようなことをします

Integration.first(:conditions => {...}, :include => :broker, :select => 'brokers.id as ignored')

代替戦略を強制する必要があります (この場合、アクティブなレコードは実際には選択オプションを無視します)。

于 2012-06-24T20:21:46.817 に答える