Rails中的高级SQL

我有2个型号

class User < AR has_many :friends end class Friend < AR # has a name column end 

我需要找到所有与’Joe’和’Jack’都是朋友的用户

知道我怎么能在铁轨上做到这一点?

一种选择是将每个名称作为单个INNER JOINS的参数。 在SQL中它将是这样的:

 SELECT users.* FROM users INNER JOIN friends AS f1 ON users.id = f1.user_id AND f1.name = 'Joe' INNER JOIN friends AS f2 ON users.id = f2.user_id AND f2.name = 'Jack' 

因为它是INNER JOINS,所以它只显示用户表可以用f1和f2连接的结果。

要在Rails中使用它,也许可以这样做:

 class User < AR has_many :friends def self.who_knows(*friend_names) joins((1..friend_names.length).map{ |n| "INNER JOIN friends AS f#{n} ON users.id = f#{n}.user_id AND f#{n}.name = ?" }.join(" "), *friend_names) }) end end 

你可以这样打电话:

 @users = User.who_knows("Joe", "Jack") 

可能的方法: User.all(:joins => :friends, :conditions => ["friends.name IN (?,?)", "Joe", "Jack"], :group => "users.id")然后遍历数组以查找有2个朋友的用户。

这是我试图为自己解决类似问题时得到的最佳解决方案。 如果你找到了在纯sql或ActiveRecord中做到这一点的方法 – 请告诉我!

尽管使用DanneManne建议的硬编码SQL通常会起作用,并且可能是您想要的方式,但它不一定是可组合的。 只要您对表名进行了硬编码,就会遇到将问题与ActiveRecord可能决定对表进行别名的其他查询相结合的问题。

因此,以一些额外的复杂性为代价,我们可以使用一些ARel解决这个问题,如下所示:

 f = Friend.arel_table User. where(:id=>f.project(:user_id).where(f[:name].eq('Joe'))). where(:id=>f.project(:user_id).where(f[:name].eq('Jack'))) 

这将使用一对子查询来完成这项工作。

我相当肯定有一个使用连接的ARel解决方案,但是我可以弄清楚如何在ARel中编写该查询,而不是如何使用该查询作为ActiveRecord查询的基础来获取用户模型实例。