2013-11-25 11 views
46

Sto provando a fare qualcosa che ho pensato sarebbe stato semplice ma sembra non esserlo.Trova tutti i record che hanno un conteggio di un'associazione maggiore di zero

Ho un modello di progetto che ha molti posti vacanti.

class Project < ActiveRecord::Base 

    has_many :vacancies, :dependent => :destroy 

end 

Voglio ottenere tutti i progetti che hanno almeno 1 posto vacante. Ho provato qualcosa di simile:

Project.joins(:vacancies).where('count(vacancies) > 0') 

ma si dice

SQLite3::SQLException: no such column: vacancies: SELECT "projects".* FROM "projects" INNER JOIN "vacancies" ON "vacancies"."project_id" = "projects"."id" WHERE ("projects"."deleted_at" IS NULL) AND (count(vacancies) > 0).

risposta

29

joins utilizza un inner join di default in modo da utilizzare Project.joins(:vacancies) volontà in effetti restituire solo i progetti che hanno un posto vacante associato.

UPDATE:

Come sottolineato da @mackskatz nel commento, senza una clausola di group, il codice di cui sopra tornerà progetti duplicati per i progetti con più di un posti vacanti. Per rimuovere i duplicati, utilizzare

Project.joins(:vacancies).group('projects.id') 
+0

Tuttavia, senza applicare una clausola group by restituire più oggetti Project per progetti che hanno più di un posto vacante. – mackshkatz

19

Sì, vacancies non è un campo nel join. Credo che si desidera:

Project.joins(:vacancies).group("projects.id").having("count(vacancies.id)>0") 
+0

Da dove viene: vengono le vacanze? –

-4

L'errore sta dicendo che i posti vacanti non è una colonna in progetti, in fondo.

Questo dovrebbe funzionare

Project.joins(:vacancies).where('COUNT(vacancies.project_id) > 0') 
+4

'le funzioni di aggregazione' non sono consentite in WHERE' – squixy

87

1) Per i progetti con almeno 1 posto vacante:

Project.joins(:vacancies).group('projects.id') 

2) Per i progetti con più di 1 posto vacante:

Project.joins(:vacancies).group('projects.id').having('count(project_id) > 1') 

3) Oppure, se il modello Vacancy imposta contatore cache:

belongs_to :project, counter_cache: true 

allora questo funzionerà, troppo:

Project.where('vacancies_count > ?', 1) 

regola di inflessione per vacancy può essere necessario specified manually?

2
# None 
Project.joins(:vacancies).group('projects.id').having('count(vacancies) = 0') 
# Any 
Project.joins(:vacancies).group('projects.id').having('count(vacancies) > 0') 
# One 
Project.joins(:vacancies).group('projects.id').having('count(vacancies) = 1') 
# More than 1 
Project.joins(:vacancies).group('projects.id').having('count(vacancies) > 1') 
0

senza magia molto Rails, si può fare:

Project.where('(SELECT COUNT(*) FROM vacancies WHERE vacancies.project_id = projects.id) > 0') 

Questo tipo di condizioni funzionerà in tutte le versioni Rails come gran parte del lavoro è fatto direttamente sul lato DB. Inoltre, il metodo di concatenamento .count funzionerà correttamente. Sono stato masterizzato da query come Project.joins(:vacancies) prima. Naturalmente, ci sono pro e contro in quanto non è agnostico di DB.

+0

Questo è molto più lento del metodo join e group, poiché la subquery 'select count (*) ..' verrà eseguita per ciascun progetto. – YasirAzgar