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不是很强,但如果有人指出我正确的方向,我可能会想出来。
答案 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_id
和time
列,此解决方案非常有效。
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')
您可以根据需要编辑条件。