Wykonywanie sprzężenia wewnętrznego do tabeli has_many w połączeniu z group
lub uniq
jest potencjalnie bardzo nieefektywne, aw SQL byłoby lepiej zaimplementowane jako półzłączenie, które używa EXISTS
skorelowanego podzapytania.
Dzięki temu optymalizator zapytań może sondować tabelę wakatów w celu sprawdzenia, czy istnieje wiersz z poprawnym project_id. Nie ma znaczenia, czy istnieje jeden wiersz, czy milion, które mają ten identyfikator projektu.
Nie jest to takie proste w Railsach, ale można to osiągnąć za pomocą:
Project.where(Vacancies.where("vacancies.project_id = projects.id").exists)
Podobnie znajdź wszystkie projekty, w których nie ma wolnych miejsc pracy:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").exists)
Edycja: w ostatnich wersjach Railsów otrzymujesz ostrzeżenie o wycofaniu, informujące, że nie możesz polegać na exists
delegowaniu na arel. Napraw to za pomocą:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").arel.exists)
Edycja: jeśli nie czujesz się komfortowo z surowym SQL, spróbuj:
Project.where.not(Vacancies.where(Vacancy.arel_table[:project_id].eq(Project.arel_table[:id])).arel.exists)
Możesz zrobić to mniej bałaganem, dodając metody klasowe, aby ukryć użycie arel_table
, na przykład:
class Project
def self.id_column
arel_table[:id]
end
end
... więc ...
Project.where.not(
Vacancies.where(
Vacancy.project_id_column.eq(Project.id_column)
).arel.exists
)