2010-09-27 52 views
0

這裏的當前查詢:如何修改複雜的find_by_sql查詢W /聯合成軌3

@feed = RatedActivity.find_by_sql(["(select *, null as queue_id, 3 as model_table_type from rated_activities where user_id in (?)) " + 
"UNION (select *, null as queue_id, null as rating, 2 as model_table_type from watched_activities where user_id in (?)) " + 
"UNION (select *, null as rating, 1 as model_table_type from queued_activities where user_id in (?)) " +"ORDER BY activity_datetime DESC limit 100", friend_ids, friend_ids, friend_ids]) 

現在,這是一個有點雜牌的,因爲實際上有模型設置爲:

class RatedActivity < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :media 
end 

class QueuedActivity < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :media 
end 

class WatchedActivity < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :media 
end 

很想知道如何在rails 3.0中使用activerecord來實現與我在那裏的瘋狂工會所做的基本相同的事情。

回答

2

聽起來你應該將這三個單獨的模型合併成一個模型。然後,基於該模型的屬性,諸如「觀看」,「排隊」或「評級」等狀態都是隱含的。

class Activity < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :media 

    scope :for_users, lambda { |u| 
    where("user_id IN (?)", u) 
    } 
    scope :rated, where("rating IS NOT NULL") 
    scope :queued, where("queue_id IS NOT NULL") 
    scope :watched, where("watched IS NOT NULL") 
end 

然後,您可以撥打Activity.for_users(friend_ids)讓所有三組與您試圖上面來完成...或者你可以撥打Activity.for_users(friend_ids).rated(或排隊或觀看)得到的只是一組。這樣,您的所有活動邏輯就被整合到一個地方。您的查詢變得更簡單(也更高效),您不必維護三種不同的模型。

+0

會喜歡這樣做,除非我正在使用舊數據庫。我在這件事上沒有選擇。 – Paul 2010-09-27 08:05:23

1

我認爲您的當前解決方案在傳統數據庫中可以使用。作爲本地查詢,它也是最有效的,因爲您的DBMS會盡一切努力(聯合,排序,限制)。

如果你真的想在不改變模式的情況下襬脫SQL UNION,那麼你可以將union移到Ruby數組總和 - 但這可能會變慢。

result = RatedActivity. 
      select("*, null as queue_id, 3 as model_table_type"). 
      where(:user_id=>friend_ids). 
      limit(100).all + 
     QueuedActivity... 

最後,你需要排序,並限制該產品與

result.sort(&:activity_datetime)[0..99] 

這是概念恰恰證明,當你看到它是低效的一些點(3個查詢,在Ruby中排序,限制) 。我會留在find_by_sql