1

Rails 3ActiveRecordクエリインターフェイスで問題が発生しています。ルックアップテーブル(lookups)、メインテーブル(through_references)、およびthrough_tablesと呼ばれるthrough/joinテーブルがあります。したがって、これは、has_many:throughを使用して設定したHABTM構成です。

更新:ここで特に注意するのは、これらの結合を行うとき、レコードのフィルタリングを提供するためにIDで結合していることです。これはActiveRecordクエリインターフェイスでは機能しないようです。私の苦難の残酷な詳細を見たくない場合は、スキップして以下の私の回避策を見ることができます。

また、いくつかのメインアイテム(through_referencesテーブル)にルックアップアイテムの任意の組み合わせを含めることができ、チェックボックスを介して関連するルックアップアイテムをクリックできるようにする必要があります。

コードをgithubに投稿しました。githubのソースコードについては、さらに多くの説明があります。結果を確認するには、ルックアップのインデックスページに移動します。スキャフォールドコードを使用してレコードを作成する必要があることに注意してください。

また、コードをherokuで実行し、さらに説明と例を示します。

class Lookup < ActiveRecord::Base
  has_many :fk_references
  has_many :through_tables
  has_many :through_references, :through => :through_tables
  attr_accessible :name, :value
end

class ThroughTable < ActiveRecord::Base
  belongs_to :through_reference
  belongs_to :lookup
  attr_accessible :description, :through_reference_id, :lookup_id
end

class ThroughReference < ActiveRecord::Base
  has_many :through_tables
  has_many :lookups, :through => :through_tables
  attr_accessible :description
end

すべてのルックアップアイテムとそれに対応するメインアイテムのリストが必要な場合は、「ルックアップ」テーブルとメインアイテム(through_references)テーブルを左に結合できます。対応するSQL:

SELECT * FROM lookups
  LEFT OUTER JOIN through_tables ON (lookups.id = through_tables.lookup_id AND through_tables.through_reference_id = 1)
  LEFT OUTER JOIN through_references ON through_references.id = through_tables.through_reference_id
  ORDER BY lookups.id

返されたレコード:

1;“Lookup Item 1”;“1”;“2012-06-06 17:14:40.819791”;“2012-06-06 17:14:40.819791”;1;1;1;“Main Item 1 has Lookup item 1”;“2012-06-06 17:17:31.355425”;“2012-06-06 17:17:31.355425”;1;“Main Item 1”;“2012-06-06 17:16:30.004375”;“2012-06-06 17:16:30.004375”

2;“Lookup Item 2”;“2”;“2012-06-06 17:14:59.584756”;“2012-06-06 17:14:59.584756”;;;;“”;“”;“”;;“”;“”;“”

3;“Lookup Item 3”;“3”;“2012-06-06 17:15:14.700239”;“2012-06-06 17:15:14.700239”;2;1;3;“Main Item 1 has Lookup item 3”;“2012-06-06 17:17:53.169715”;“2012-06-06 17:17:53.169715”;1;“Main Item 1”;“2012-06-06 17:16:30.004375”;“2012-06-06 17:16:30.004375”

これは私が期待したことです。

===カスタム左結合を使用したアクティブレコードクエリインターフェイス

Lookup.joins(“LEFT OUTER JOIN through_tables ON (lookups.id = through_tables.lookup_id AND through_tables.through_reference_id = 1)” ).includes(:through_references).order(‘lookups.id’)

Active Recordクエリインターフェイスから返されるもの(Active Record階層を下に移動することに注意してください):

Lookup ID Lookup Name Lookup Value Through Table ID Through Table Description Main Item ID Main Item Description
1 Lookup Item 1 1 1 Main Item 1 has Lookup item 1 1 Main Item 1
1 Lookup Item 1 1 3 Main Item 2 has Lookup item 1 2 Main Item 2
2 Lookup Item 2 2 4 Main Item 2 has Lookup item 2 2 Main Item 2
3 Lookup Item 3 3 2 Main Item 1 has Lookup item 3 1 Main Item 1

これは私が期待したものではありません。

ここにあるのは、単純な左結合(AND句なし)と同じです。これは、AND句がActiveRecordクエリインターフェイスで無視されていることを示しています。

===find_by_sqlアプローチを使用したアクティブレコードクエリインターフェイス

Lookup.find_by_sql("SELECT * FROM lookups LEFT OUTER JOIN through_tables ON (through_tables.lookup_id = lookups.id AND through_tables.through_reference_id = 1) LEFT OUTER JOIN through_references ON through_references.id = through_tables.through_reference_id ORDER BY lookups.value, through_references.id" )

Active Recordクエリインターフェイスから返されるもの(Active Record階層を下に移動することに注意してください)::

Lookup ID   Lookup Name     Lookup Value    Through Table ID    Through Table Description   Main Item ID    Main Item Description
1   Lookup Item 1   1   3   Main Item 2 has Lookup item 1   2   Main Item 2
1   Lookup Item 1   1   1   Main Item 1 has Lookup item 1   1   Main Item 1
    Lookup Item 2   2   No through_tables entry
1   Lookup Item 3   3   3   Main Item 2 has Lookup item 1   2   Main Item 2
1   Lookup Item 3   3   1   Main Item 1 has Lookup item 1   1   Main Item 1

ここでの結果はクレイジーです!

これはバグですか、これは意図された効果ですか、それとも何かが足りませんか?

2つの結果セットを生成し、それらをコードでマージすることなく、これを行うためのクリーンな方法があることを願っています。

4

2 に答える 2

1

回避策を見つけました。問題は、Active Record が ID (LEFT OUTER JOIN xyz ON xyz.id = ID) でフィルタリングする結合を認識しないことです。

私の回避策は、ID をパラメーターとして受け取り、データベースで結合を行い、適切なフラット レコードセットを返すストアド プロシージャまたは関数を作成することです。

参照: Heroku デモページ (一番下にスキップ)

これは回避策であり、アクティブなレコードとは関係がないため、これを解決策としてマークしていないことに注意してください。

于 2013-01-23T17:44:41.353 に答える
0

さて、githubプロジェクトを読んで、私はこれを見ます:

私が本当にやりたいのは、すべてのルックアップアイテムのリストを用意し、一致するメインアイテムがある場合は、返されるレコードに追加することです。そうでない場合は、nullが必要です。これは私が10年以上使ってきたテクニックです。

問題はまさにそのようにしたいということだと思います。Railsが熱心にロードして処理するのがより自然な場合は、1つの大規模な結合ですべてをフェッチすることに固執することになります。

私がすることは次のようなものです:

Lookup.where(..必要な条件をここに挿入...)。includes(:through_tables)

次に、ActiveQueryは1つのクエリですべてのルックアップをフェッチし、積極的な読み込みを使用して、includesステートメントで指定されたすべての関連付けをフェッチします(関連付けごとに1つのクエリ)。

結合が悪いと言っているのではなく、これがレールでそれを行うためのより自然な方法であると言っていることに注意してください。私はプリローダーhttp://apidock.com/rails/ActiveRecord/Associations/Preloaderを使用して、何を熱心にロードするかについての決定と、どのデータをフェッチするかについての決定を区別するのが好きです。これはコントローラーで役立つと思います。モデルに条件を決定させますが、熱心にロードする必要があるオブジェクトをコントローラーに決定させます。

HTH

于 2012-06-07T18:36:19.357 に答える