如何修复复杂的find_by_sql查询w / union到rails 3

时间:2010-09-27 06:04:48

标签: ruby-on-rails activerecord

这是当前的查询:

@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 个答案:

答案 0 :(得分: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(或排队或观看)以获得一个群组。这样,您的所有Activity逻辑都合并到一个位置。您的查询变得更简单(也更有效),您不必维护三种不同的模型。

答案 1 :(得分: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