repetir - subconsultas sql
Encuentra todos los registros que tienen un recuento de una asociaciĆ³n mayor que cero (6)
1) Para obtener Proyectos con al menos 1 vacante:
Project.joins(:vacancies).group(''projects.id'')
2) Para obtener Proyectos con más de 1 vacante:
Project.joins(:vacancies).group(''projects.id'').having(''count(project_id) > 1'')
3) O, si el modelo de Vacancy
establece el contador de caché:
belongs_to :project, counter_cache: true
entonces esto también funcionará:
Project.where(''vacancies_count > ?'', 1)
La regla de inflexión para vacancy
puede necesitar ser especificada manualmente ?
Intento hacer algo que pensé que sería simple, pero parece que no lo es.
Tengo un modelo de proyecto que tiene muchas vacantes.
class Project < ActiveRecord::Base
has_many :vacancies, :dependent => :destroy
end
Quiero obtener todos los proyectos que tienen al menos 1 vacante. Intenté algo como esto:
Project.joins(:vacancies).where(''count(vacancies) > 0'')
pero 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)
.
El error es decirle que las vacantes no son una columna en los proyectos, básicamente.
Esto debería funcionar
Project.joins(:vacancies).where(''COUNT(vacancies.project_id) > 0'')
Sí, las vacancies
no son un campo en la unión. Creo que quieres:
Project.joins(:vacancies).group("projects.id").having("count(vacancies.id)>0")
Sin mucha magia de Rails, puedes hacer:
Project.where(''(SELECT COUNT(*) FROM vacancies WHERE vacancies.project_id = projects.id) > 0'')
Este tipo de condiciones funcionará en todas las versiones de Rails ya que gran parte del trabajo se realiza directamente en el lado de la base de datos. Además, el método de encadenamiento .count
también funcionará muy bien. Me he quemado por consultas como Project.joins(:vacancies)
antes. Por supuesto, hay pros y contras ya que no es DB agnóstico.
join utiliza una unión interna por defecto, por lo que usar Project.joins(:vacancies)
en efecto solo devolverá proyectos que tengan una vacante asociada.
ACTUALIZAR:
Como señala @mackskatz en el comentario, sin una cláusula de group
, el código anterior devolverá proyectos duplicados para proyectos con más de una vacante. Para eliminar los duplicados, use
Project.joins(:vacancies).group(''projects.id'')
# 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'')