我真的想在活动记录
的帮助下进行以下查询(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代码。
答案 0 :(得分:7)
您的问题可能无法解决,例如:
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'))
它将生成以下形式的SQL:
SELECT "people".*
FROM "people"
WHERE "people"."id" in (SELECT "people"."id" FROM ...)
AND "people"."id" in (SELECT ...)
AND ...
只要每个子查询返回其结果集中匹配人员的id,您就可以根据任何条件/连接等使用上述方法根据需要创建任意数量的子查询。
每个子查询结果集将被“和”在一起,从而将匹配集限制为所有子查询的交集。
更新
对于那些使用删除了scoped
的AR4的人,我的另一个答案提供了语义上等效的scoped
polyfil,all
不是等效的替代品,尽管AR文档建议。在这里回答:With Rails 4, Model.scoped is deprecated but Model.all can't replace it
答案 1 :(得分:1)
我正在努力解决同样的问题,并且只找到了一个解决方案:针对同一个关联的多个联接。由于我正在构建连接的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)
答案 2 :(得分:0)
我对相交并不熟悉,但有没有理由你不能将其简化为单个查询并改为使用IN
?:
People.where(:services => {:type => [1,2]}).joins(:cities => :services)
更新:你可以链接方法,他们将与AND加入:
People.where(:services => {:type => 1}).where(:services => {:type => 2}).joins(:cities => :services)