【问题标题】:Query intersection with activerecord查询与 activerecord 的交集
【发布时间】:2012-08-07 02:41:52
【问题描述】:

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

(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​​ 代码。

【问题讨论】:

    标签: mysql ruby-on-rails ruby-on-rails-3 activerecord


    【解决方案1】:

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

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

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

    更新

    对于那些使用 AR4 并删除了scoped 的人,我的另一个答案提供了语义上等效的scoped polyfil,尽管 AR 文档建议,all 并不是等效的替代品。在这里回答:With Rails 4, Model.scoped is deprecated but Model.all can't replace it

    【讨论】:

    • 请注意,scoped ActiveRecord 类方法(在 reduced(scoped) 中使用)已在 Rails 4.1 中删除,但原始解决方案通过替换 all 来工作。
    • 请注意,第一种方法也可能返回服务类型为[1, 3]的服务。
    【解决方案2】:

    我在同一个问题上苦苦挣扎,但只找到了一个解决方案:针对同一个关联进行多个联接。这可能不太像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-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多