Rails查询具有关联条件的多个主键

时间:2011-03-03 05:31:27

标签: mysql ruby-on-rails activerecord

Active Record中是否有一种方法可以构建一个单个查询来为多个主键执行条件连接?

说我有以下型号:

Class Athlete < ActiveRecord::Base
  has_many :workouts
end

Class Workout < ActiveRecord::Base
  belongs_to :athlete
  named_scope :run, :conditions => {:type => "run"}
  named_scope :best, :order => "time", :limit => 1
end

有了这个,我可以生成一个查询来获得运动员的最佳运行时间:

  Athlete.find(1).workouts.run.best

如何使用单个查询为团队中的每位运动员获得最佳运行时间?

以下方法不起作用,因为它只将命名范围应用于整个阵列,为所有运动员返回单一最佳时间:

Athlete.find([1,2,3]).workouts.run.best

以下作品。但是,对于大量的运动员来说,它不具有可扩展性,因为它会为每位运动员生成单独的查询:

[1,2,3].collect {|id| Athlete.find(id).workouts.run.best}

有没有办法使用Active Record查询界面和关联生成单个查询

如果没有,有人可以建议我可以用于find_by_SQL的SQL查询模式吗?我必须承认我对SQL不是很强,但如果有人指出我正确的方向,我可能会想出来。

2 个答案:

答案 0 :(得分:2)

以最佳时间获取Workout对象:

athlete_ids = [1,2,3]
# Sanitize the SQL as we need to substitute the bind variable
# this query will give duplicates
join_sql    = Workout.send(:santize_sql, [ 
    "JOIN (
      SELECT a.athlete_id, max(a.time) time 
      FROM   workouts a
      WHERE  a.athlete_id IN (?)
      GROUP BY a.athlete_id
    ) b ON b.athlete_id = workouts.athlete_id AND b.time = workouts.time", 
    athlete_ids])


Workout.all(:joins => join_sql, :conditions => {:athlete_id => })

如果您需要每个用户最佳的锻炼时间,那么:

Athlete.max("workouts.time", :include => :workouts, :group => "athletes.id", 
 :conditions => {:athlete_id => [1,2,3]}))

这将返回OrderedHash

{1 => 300, 2 => 60, 3 => 120}

修改1

以下解决方案可避免以相同的最佳时间返回多次锻炼。如果索引athlete_idtime列,此解决方案非常有效。

Workout.all(:joins => "LEFT OUTER JOIN workouts a 
  ON workouts.athlete_id  = a.athlete_id AND 
     (workouts.time < b.time OR workouts.id < b.id)",
  :conditions => ["workouts.athlete_id = ? AND b.id IS NULL", athlete_ids]
)

阅读此article以了解此查询的工作原理。 workouts.id < b.id中的上次检查(JOIN)确保在最佳时间有多个匹配项时仅返回一行。如果运动员的最佳时间不止一场比赛,则返回最高身份的训练(即最后一次训练)。

答案 1 :(得分:1)

当然以下将不起作用

Athlete.find([1,2,3]).workouts.run.best

因为Athlete.find([1,2,3])返回一个数组,你不能调用Array.workouts

您可以尝试这样的事情:

 Workout.find(:first, :joins => [:athlete], :conditions => "athletes.id IN (1,2,3)", :order => 'workouts.time DESC')

您可以根据需要编辑条件。