Rails 查询具有关联条件的多个主键
Posted
技术标签:
【中文标题】Rails 查询具有关联条件的多个主键【英文标题】:Rails query on multiple primary keys with conditions on association 【发布时间】:2011-03-03 05:31:26 【问题描述】:在 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 方面不是很擅长,但如果有人能指出我正确的方向,我可能会弄清楚。
【问题讨论】:
【参考方案1】:要获得最佳时间的锻炼对象:
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 以了解此查询的工作原理。 JOIN
中的最后一次检查 (workouts.id < b.id
) 确保在最佳时间有多个匹配项时仅返回一行。当一个运动员的最佳时间有多个匹配时,返回具有最高 id 的锻炼(即最后一次锻炼)。
【讨论】:
谢谢。我收到完整对象查询的 SQL 语法错误。但是,我认为您通过 GROUP BY 子句为我指明了正确的方向。我将借此机会更聪明地使用 GROUP BY 和 HAVING 处理复杂的续集连接。 很好,成功了,谢谢。虽然语法错误很有帮助,因为它迫使我阅读一些 mysql 教程。是时候停止盲目依赖 Rails 并在底层数据库查询上变得聪明了。 关于完整查询的一个问题。假设我有大量的锻炼和运动员。使用右表(完整的 Workout 对象)、左表内部(例如,作为 SELECT 的 WHERE 条件)或同时使用这两种情况下的运动员 ID 条件,查询会更有效吗? 由答案更新,看看吧。 非常好,谢谢。事实证明,相同的模式对于优化我遇到的其他几个查询问题很有用。【参考方案2】:当然跟随是行不通的
运动员.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')您可以根据需要编辑条件。
【讨论】:
你说得对,我的伪代码很草率。实际上,我为 Workout 创建了另一个命名范围,由 调用,它带有一个 lambda 条件来接收一组运动员 ID。然后我可以调用 Workout.all.by([1,2,3]).run.best。但这不起作用,只返回所有运动员的最佳时间,而不是每个运动员的最佳时间。 根据我的测试,您的建议做同样的事情:返回所有运动员的单一最佳时间,而不是每个运动员的单独最佳时间。不过,我们非常感谢您的帮助。以上是关于Rails 查询具有关联条件的多个主键的主要内容,如果未能解决你的问题,请参考以下文章