sql >> Base de Datos >  >> RDS >> Mysql

Consulta de intersección con activerecord

Su pregunta probablemente se pueda resolver sin intersección, algo así como:

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

Sin embargo, el siguiente es un enfoque general que uso para construir intersecciones como consultas en 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'))

Generará SQL de la forma:

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

Puede crear tantas subconsultas como sea necesario con el enfoque anterior en función de cualquier condición/unión, etc., siempre que cada subconsulta devuelva la identificación de una persona coincidente en su conjunto de resultados.

Cada conjunto de resultados de subconsulta se combinará con AND, restringiendo así el conjunto coincidente a la intersección de todas las subconsultas.

ACTUALIZAR

Para aquellos que usan AR4 donde scoped fue eliminado, mi otra respuesta proporciona un scoped semánticamente equivalente polyfil que all no es un reemplazo equivalente a pesar de lo que sugiere la documentación AR. Responda aquí:Con Rails 4, Model.scoped está obsoleto pero Model.all no puede reemplazarlo