2011-03-16 10 views
12
class Person < ActiveRecord::Base 
    has_many :pets 

    scope :with_dog, join(:pets).where("pets.type = 'Dog'") 
    scope :without_pets ??????????????????????????????????? 
end 

class Pet < ActiveRecord::Base 
    belongs_to :people 
end 

Me gustaría agregar un alcance al modelo de persona que devuelve personas que no tienen mascotas. ¿Algunas ideas? Siento que esto es obvio, pero me está escapando en este momento.Búsqueda de registros sin registros asociados en rieles 3

Respuesta

8

intentar algo como esto:

Person.joins('left outer join pets on persons.id=pets.person_id'). 
     select('persons.*,pets.id'). 
     where('pets.id is null') 

no he probado pero debe trabajar.

La idea es que estamos realizando una unión externa izquierda, por lo que los campos de mascotas serán nulos para cada persona que no tenga mascotas. Probablemente necesite incluir :readonly => false en la unión, ya que ActiveRecord devuelve objetos de solo lectura cuando se pasa una cadena a join().

+0

SQL resultante que falla:. SELECT * personas, pets.id DE exterior 'persons' combinación izquierda mascotas donde persons.id = pets.person_id donde' persons'. 'deleted' = 0 AND (pets.id es nulo) – Shagymoe

+0

¡Uy! Me equivoqué de esa unión. Debería haber sido "... en persons.id = pets.person_id", no "... where persons.id = ...". –

+0

¿Necesita el ", pets.id" en select ('personas. *, Pets.id')? Tiene el potencial de colisionar con personas.id a menos que alias la columna como esta: selecciona ('personas. *, Pets.id como pet_id') – mkirk

0

No estoy seguro de si el modelo de animal doméstico tiene un ID de la persona, pero tal vez este intento le ayuda de alguna manera

scope :with_dog, joins(:pets).where("pets.type = 'Dog'") 
scope :without_pets, joins(:pets).where("pets.person_id != persons.id") 

Actualización: Se ha corregido la consulta nombre del método de 'unirse' a 'une'.

5

La respuesta de Mark Westling es correcta. La unión externa es el camino correcto a seguir. Una unión interna (que es lo que genera el método de uniones si le pasa el nombre/símbolo de una asociación y no su propio SQL) no funcionará, ya que no incluirá a personas que no tengan una mascota.

Aquí está escrito como un ámbito de aplicación: (. Si eso no funciona, intente reemplazar 'personas' con 'gente' - No estoy seguro de lo que su nombre de la tabla es)

scope :without_pets, joins("left outer join pets on pets.person_id = persons.id").where("pets.id is null") 

21
scope :without_pets, lambda { includes(:pets).where('pets.id' => nil) } 
+0

aunque LEFT OUTER JOIN no es el propósito de 'includes', con la combinación de una consulta resultará en LEFT OUTER JOIN. – lulalala

+2

Esto funciona, independientemente de su propósito. Menos sql manuscrita que respuesta aceptada = Creo que es mejor. –

1

Debe usar un LEFT OUTER JOIN para buscar registros sin registros asociados. Aquí está una versión adaptada de un código que utilizo:

scope :without_pets, joins('LEFT OUTER JOIN pets ON people.id = pets.person_id').group('people.id').having('count(pets.id) = 0') 
Cuestiones relacionadas