Mysql
 sql >> Base de données >  >> RDS >> Mysql

Intersection de la requête avec l'enregistrement actif

Votre question peut probablement être résolue sans intersection, quelque chose comme :

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

Cependant, voici une approche générale que j'utilise pour construire des intersections comme des requêtes dans 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'))

Il générera du SQL de la forme :

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

Vous pouvez créer autant de sous-requêtes que nécessaire avec l'approche ci-dessus basée sur toutes les conditions/jointures, etc. tant que chaque sous-requête renvoie l'identifiant d'une personne correspondante dans son jeu de résultats.

Chaque ensemble de résultats de sous-requête sera combiné par AND, limitant ainsi l'ensemble correspondant à l'intersection de toutes les sous-requêtes.

MISE À JOUR

Pour ceux qui utilisent AR4 où scoped a été supprimé, mon autre réponse fournit un scoped sémantiquement équivalent polyfil qui all n'est pas un remplacement équivalent pour malgré ce que la documentation AR suggère. Répondez ici :Avec Rails 4, Model.scoped est obsolète mais Model.all ne peut pas le remplacer