Effectuer une jointure interne à la table has_many combinée avec un group
ou uniq
est potentiellement très inefficace, et en SQL, cela serait mieux implémenté comme une semi-jointure qui utilise EXISTS
une sous-requête corrélée.
Cela permet à l'optimiseur de requêtes de sonder la table des postes vacants pour vérifier l'existence d'une ligne avec le project_id correct. Peu importe qu'il y ait une ligne ou un million qui aient cet id_projet.
Ce n'est pas aussi simple dans Rails, mais peut être réalisé avec:
Project.where(Vacancies.where("vacancies.project_id = projects.id").exists)
De même, recherchez tous les projets qui n'ont pas de poste vacant:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").exists)
Edit: dans les versions récentes de Rails, vous recevez un avertissement d'obsolescence vous indiquant de ne pas compter sur la exists
délégation à arel. Corrigez cela avec:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").arel.exists)
Edit: si vous n'êtes pas à l'aise avec le SQL brut, essayez:
Project.where.not(Vacancies.where(Vacancy.arel_table[:project_id].eq(Project.arel_table[:id])).arel.exists)
Vous pouvez rendre cela moins compliqué en ajoutant des méthodes de classe pour masquer l'utilisation de arel_table
, par exemple:
class Project
def self.id_column
arel_table[:id]
end
end
... alors ...
Project.where.not(
Vacancies.where(
Vacancy.project_id_column.eq(Project.id_column)
).arel.exists
)