5

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

(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 不支持 intersect。但是,这可以解决。问题是我可以获得活动记录以输出任何接近该记录的内容。

在活动记录中,我能做的最好的事情是发出多个查询,然后使用reduce :& 它们来连接它们,但后来我得到了一个数组,而不是一个关系。这对我来说是个问题,因为我想调用诸如限制之类的东西。另外,我认为交集最好由数据库完成,而不是 ruby​​ 代码。

4

2 回答 2

8

您的问题可能无需交叉即可解决,例如:

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,您就可以根据任何条件/连接等使用上述方法创建任意数量的子查询。

Each subquery result set will be AND'ed together thus restricting the matching set to the intersection of all of the subqueries.

UPDATE

For those using AR4 where scoped was removed, my other answer provides a semantically equivalent scoped polyfil which all is not an equivalent replacement for despite what the AR documentation suggests. Answer here: With Rails 4, Model.scoped is deprecated but Model.all can't replace it

于 2013-07-12T04:38:27.047 回答
1

我在同一个问题上苦苦挣扎,只找到了一个解决方案:针对同一个关联的多个联接。这可能不太像rails-ish,因为我正在为连接构建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)
于 2013-01-23T22:00:39.067 回答