查询与activerecord的交集

我真的想在活动记录的帮助下进行以下查询

(select * from people p join cities c join services s where p.city_id = c.id and p.id = s.person_id and s.type = 1) intersect (select * from people p join cities c join services s where p.city_id = c.id and p.id = s.person_id and s.type = 2) 

问题是,首先,mysql不支持交叉。 但是,这可以解决。 问题在于我可以获得活动记录来输出甚至接近它的任何东西。

在活动记录中,我能做的最好的事情是发出多个查询然后使用reduce :&来加入它们,但后来我得到一个数组,而不是一个关系。 这对我来说是一个问题,因为我想调用限制等等。另外,我认为交叉点要由数据库完成,而不是ruby代码。

你的问题可能是没有交集的可解决的,例如:

 Person.joins(:services).where(services: {service_type: [1,2]}).group( people: :id).having('COUNT("people"."id")=2') 

但是,以下是我用于在ActiveRecord中构建交叉类似查询的一般方法:

 class Service < ActiveRecord::Base belongs_to :person def self.with_types(*types) where(service_type: types) end end class City < ActiveRecord::Base has_and_belongs_to_many :services has_many :people, inverse_of: :city end class Person < ActiveRecord::Base belongs_to :city, inverse_of: :people def self.with_cities(cities) where(city_id: cities) end def self.with_all_service_types(*types) types.map { |t| joins(:services).merge(Service.with_types t).select(:id) }.reduce(scoped) { |scope, subquery| scope.where(id: subquery) } end end Person.with_all_service_types(1, 2) Person.with_all_service_types(1, 2).with_cities(City.where(name: 'Gold Coast')) 

它将生成以下forms的SQL:

 SELECT "people".* FROM "people" WHERE "people"."id" in (SELECT "people"."id" FROM ...) AND "people"."id" in (SELECT ...) AND ... 

您可以根据任何条件/连接等使用上述方法根据需要创建任意数量的子查询,只要每个子查询在其结果集中返回匹配人员的ID即可。

每个子查询结果集将被“和”在一起,从而将匹配集限制为所有子查询的交集。

UPDATE

对于那些使用AR4去除scoped人,我的另一个答案提供了一个语义上等效的scoped polyfil,尽管AR文档建议,但它们all是等效的替代品。 在这里回答: 使用Rails 4,不推荐使用Model.scoped,但Model.all不能替换它

我正在努力解决同样的问题,并且只找到了一个解决方案:针对同一个关联的多个连接。 由于我正在构建连接的SQL字符串,所以这可能不是太多了,但我还没有找到另一种方法。 这适用于任意数量的服务类型(城市似乎没有考虑因素,因此为清晰起见省略了连接):

 s = [1,2] j = '' s.each_index {|i| j += " INNER JOIN services s#{i} ON s.person_id = people.id AND s#{i}.type_id = #{s[i]}" } People.all.joins(j) 

我不是很熟悉相交,但是有什么理由你不能简化为单个查询并使用IN代替吗?:

 People.where(:services => {:type => [1,2]}).joins(:cities => :services) 

更新:您可以链接方法,他们将与AND连接:

 People.where(:services => {:type => 1}).where(:services => {:type => 2}).joins(:cities => :services)