Mysql
 sql >> Database >  >> RDS >> Mysql

Intersezione query con activerecord

La tua domanda è probabilmente risolvibile senza intersezione, qualcosa del tipo:

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

Tuttavia, il seguente è un approccio generale che utilizzo per costruire query simili a intersezioni in 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'))

Genererà SQL del modulo:

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

Puoi creare tutte le sottoquery necessarie con l'approccio sopra in base a qualsiasi condizione/join, ecc., purché ogni sottoquery restituisca l'id di una persona corrispondente nel suo set di risultati.

Ciascun set di risultati di sottoquery verrà sottoposto a AND insieme, limitando così il set di corrispondenza all'intersezione di tutte le sottoquery.

AGGIORNAMENTO

Per coloro che utilizzano AR4 con scoped è stato rimosso, l'altra mia risposta fornisce un scoped semanticamente equivalente polyfil che all non è un sostituto equivalente nonostante ciò che suggerisce la documentazione AR. Rispondi qui:Con Rails 4, Model.scoped è deprecato ma Model.all non può sostituirlo