查询与activerecord的交集

Raf*_*ida 4 mysql activerecord ruby-on-rails ruby-on-rails-3

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

(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)
Run Code Online (Sandbox Code Playgroud)

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

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

And*_*ing 7

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

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

但是,以下是我用于在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'))
Run Code Online (Sandbox Code Playgroud)

它将生成以下形式的SQL:

SELECT "people".*
  FROM "people"
 WHERE "people"."id" in (SELECT "people"."id" FROM ...)
   AND "people"."id" in (SELECT ...)
   AND ...
Run Code Online (Sandbox Code Playgroud)

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

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

UPDATE

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