Estoy tratando de 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 conseguir todos los proyectos que tengan 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)
.
Project.joins(:vacancies).distinct
?1) Para conseguir proyectos con al menos 1 vacante:
2) Para obtener proyectos con más de 1 vacante:
3) O, si el
Vacancy
modelo establece la caché del contador:entonces esto también funcionará:
¿Es
vacancy
posible que sea necesario especificar manualmente la regla de inflexión para ?fuente
Project.joins(:vacancies).group('projects.id').having('count(vacancies.id) > 1')
? Consultando el número de vacantes en lugar de losprojects.id
,project_id
yvacancies.id
. Elegí contarproject_id
porque es el campo en el que se realiza la unión; la columna vertebral de la unión si se quiere. También me recuerda que esta es una tabla de combinación.Sí,
vacancies
no es un campo en la combinación. Creo que quieres:fuente
fuente
Realizar una unión interna a la tabla has_many combinada con un
group
ouniq
es potencialmente muy ineficiente, y en SQL esto se implementaría mejor como una semi-unión que se usaEXISTS
con una subconsulta correlacionada.Esto permite al optimizador de consultas sondear la tabla de vacantes para verificar la existencia de una fila con el project_id correcto. No importa si hay una fila o un millón que tienen ese project_id.
Eso no es tan sencillo en Rails, pero se puede lograr con:
Del mismo modo, busque todos los proyectos que no tengan vacantes:
Editar: en las versiones recientes de Rails, recibe una advertencia de desaprobación que le indica que no dependa de
exists
ser delegado a arel. Arregle esto con:Editar: si no se siente cómodo con SQL sin formato, intente:
Puede hacer que esto sea menos complicado agregando métodos de clase para ocultar el uso de
arel_table
, por ejemplo:... entonces ...
fuente
Vacancy.where("vacancies.project_id = projects.id").exists?
producetrue
ofalse
.Project.where(true)
es unArgumentError
.Vacancy.where("vacancies.project_id = projects.id").exists?
no se ejecutará; generará un error porque laprojects
relación no existirá en la consulta (y tampoco hay un signo de interrogación en el código de muestra anterior). Entonces, descomponer esto en dos expresiones no es válido y no funciona. Recientemente, RailsProject.where(Vacancies.where("vacancies.project_id = projects.id").exists)
plantea una advertencia de desaprobación ... Actualizaré la pregunta.En Rails 4+, también puedes usar includes o eager_load para obtener la misma respuesta:
fuente
Creo que hay una solución más sencilla:
fuente
Sin mucha magia de Rails, puedes hacer:
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
.count
método de encadenamiento también funcionará bien. Me han quemado consultas comoProject.joins(:vacancies)
antes. Por supuesto, existen pros y contras, ya que no es independiente de DB.fuente
También puede usar
EXISTS
con enSELECT 1
lugar de seleccionar todas las columnas de lavacancies
tabla:fuente
El error es decirte que las vacantes no es una columna en los proyectos, básicamente.
Esto debería funcionar
fuente
aggregate functions are not allowed in WHERE