2009-04-25 25 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 

連接動物給他人。中檢索協會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) 

而且我不能找到一種方法來創建與該導軌適當的has_many關係。顯然,沒有辦法爲has_many提供連接條件。

我目前使用finder_sql:

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的巨大劣勢。例如:

@animal.friends.first 

將執行finder_sql無極限,獲取數千行,然後以第一陣列的(和失去幾珍貴/REQ)。

我想這是從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 

這是從我的頭,而不是測試。另外,你可能需要一些插件來處理rails的視圖(比如「redhillonrails-core」)。

+0

謝謝,我喜歡這個主意!我會盡快測試並通知您。 – Gravis 2009-05-27 09:51:43

0

兩種方式創造出許多在活動記錄許多關係has_and_belongs_to_many和的has_many:通過。 This site批評了兩者之間的差異。您不必使用這些方法編寫任何SQL。

+0

嗨,感謝您的鏈接!無論如何,habtm不允許任何一種加入條件:( 因此,我將不得不使用條件進行連接,並且由於我有超過100.000行(〜400.000),我希望有一個真正優化的SQL查詢。 使用habtm,我只會得到一半的結果,因爲我在foreign_key和association_foreign_key中尋找關係。謝謝 – Gravis 2009-04-26 22:24:26

1

有一個插件可以做你想做的。

有一篇關於它的帖子here

有一個替代here

兩者都允許你做連接條件,只是使用延遲初始化。 所以你可以使用動態條件。我發現前者更漂亮,但如果你不想安裝插件,你可以使用後者。