2009-04-25 9 views
0

私は関係テーブルを持っている:定義できません:has_many関係の条件を結合しますか?

create_table "animal_friends", :force => true do |t| 
    t.integer "animal_id" 
    t.integer "animal_friend_id" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    t.integer "status_id",  :default => 1 
    end 

は、他の人に動物を結びます。

SELECT animals.* 
from animals join animal_friends as af 
    on animals.id = 
    case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end 
WHERE #{id} in (af.animal_id, af.animal_friend_id) 

そして、私はこれで、レールで適切にhas_manyの関係を作成する方法を見つけることができません:SQLに関連付けを盗んするための最良の方法です。どうやら、has_manyの結合条件を提供する方法はありません。

has_many :friends, :class_name => "Animal", :finder_sql => 'SELECT animals.* from animals join animal_friends as af on animals.id = case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end ' + 
'WHERE #{id} in (af.animal_id, af.animal_friend_id) and status_id = #{Status::CONFIRMED.id}' 

をしかし、この方法では、ActiveRecordの魔法を破るの大きな欠点があります。

私は現在finder_sqlを使用しています。例えば:

@animal.friends.first 

は、配列の最初の(およびいくつかの貴重秒を/REQを失う)を取って、数千行をフェッチ、制限なしfinder_sqlを実行します。

私はそれがARから欠落している機能だと思うが、私は必ず最初にしたいと思います:)アクティブレコードで多くの関係に多くを作成するための おかげ

答えて

2

これを解決するには、データベースレベルでビューを作成します。これは、どちらの場合でも正しい方法です。

CREATE VIEW with_inverse_animal_friends (
    SELECT id, 
     animal_id, 
     animal_friend_id, 
     created_at, 
     updated_at, 
     status_id 
    FROM animal_friends 
    UNION 
    SELECT id, 
     animal_friend_id AS animal_id, 
     animal_id AS animal_friend_id, 
     created_at, 
     updated_at, 
     status_id 
    FROM animal_friends 
) 

あなたが関係を持つ友人のための二重のエントリあなたがこれを行うことが両方の方法を持っているしたくない場合は、次の

CREATE VIEW unique_animal_friends (
    SELECT MIN(id), animal_id, animal_friend_id, MIN(created_at), MAX(updated_at), MIN(status_id) 
    FROM 
    (SELECT id, 
      animal_id, 
      animal_friend_id, 
      created_at, 
      updated_at, 
      status_id 
     FROM animal_friends 
    UNION 
    SELECT id, 
      animal_friend_id AS animal_id, 
      animal_id AS animal_friend_id, 
      created_at, 
      updated_at, 
      status_id 
     FROM animal_friends) AS all_animal_friends 
    GROUP BY animal_id, animal_friend_id 
) 

をあなたは相反する二つがある場合に使用するSTATUS_IDかを決定するための方法が必要になりますもの。私はMIN(status_id)を選んだが、それはおそらくあなたが望むものではない。 Railsでは

あなたは今、これを行うことができます。

class Animal < ActiveRecord::Base 
    has_many :unique_animal_friends 
    has_many :friends, :through => :unique_animal_friends 
end 

class UniqueAnimalFriend < ActiveRecord::Base 
    belongs_to :animal 
    belongs_to :friend, :class_name => "Animal" 
end 

これが私の頭の外にあるとテストされていません。また、 "redhillonrails-core"のように、レールでのビュー処理のためにプラグインが必要な場合があります。

+0

ありがとう、私はアイデアが好き!できるだけ早くテストしてお知らせします。 – Gravis

+0

これに返信する前にloooooooongの遅れにご迷惑をおかけして申し訳ありませんが、私は最終的にあなたのソリューションをテストする時間がありました。 – Gravis

0

二つの方法がhas_and_belongs_to_manyアソシエーションとhas_manyのです:経由。 This siteは、この2つの違いを批判します。これらのメソッドを使用してSQLを記述する必要はありません。

+0

こんにちは、リンクありがとう!とにかく、habtmはどんな種類の結合条件も許可していません:( したがって、私は:条件を使って結合をしなければなりません.100,000以上の行(〜400.000)を持っているので、本当に最適化されたSQLクエリ。 habtmを使うと、私はforeign_keyとassociation_foreign_keyの関係も探しているので、結果の半分しか得られません。おかげで、 – Gravis

1

あなたが望むことをするプラグインがあります。

投稿はhereです。

代替手段hereがあります。

両方とも、条件を結合することを許可し、遅延初期化のみを使用しています。 動的条件を使用することができます。前者はもっときれいですが、プラグインをインストールしたくない場合は後者を使うことができます。

関連する問題