Я пытаюсь сделать что-то, что, как я думал, будет простым, но, похоже, это не так.
У меня есть модель проекта, в которой много вакансий.
class Project < ActiveRecord::Base
has_many :vacancies, :dependent => :destroy
end
Я хочу получить все проекты, в которых есть хотя бы 1 вакансия. Я пробовал примерно так:
Project.joins(:vacancies).where('count(vacancies) > 0')
но он говорит
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) Чтобы получить проекты с минимум 1 вакансией:
2) Чтобы получить проекты с более чем 1 вакансией:
3) Или, если
Vacancy
модель устанавливает кеш счетчика:тогда это тоже будет работать:
Правило перегиба для
vacancy
может потребоваться указать вручную ?источник
Project.joins(:vacancies).group('projects.id').having('count(vacancies.id) > 1')
? Запрос количества вакансий вместо идентификаторов проектовprojects.id
,project_id
иvacancies.id
. Я выбрал подсчет,project_id
потому что это поле, в котором выполняется соединение; позвоночник соединения, если хотите. Это также напоминает мне, что это соединительный стол.Ага,
vacancies
это не поле в объединении. Я считаю, что вы хотите:источник
источник
Выполнение внутреннего соединения с таблицей has_many в сочетании с
group
илиuniq
потенциально очень неэффективно, и в SQL это было бы лучше реализовать как полусоединение, которое используетсяEXISTS
с коррелированным подзапросом.Это позволяет оптимизатору запросов проверять таблицу вакансий, чтобы проверить наличие строки с правильным project_id. Неважно, есть ли одна строка или миллион с этим project_id.
Это не так просто в Rails, но может быть достигнуто с помощью:
Аналогичным образом найдите все проекты, в которых нет вакансий:
Изменить: в последних версиях Rails вы получаете предупреждение об устаревании, говорящее вам не полагаться на
exists
делегирование в arel. Исправьте это с помощью:Изменить: если вам неудобно использовать необработанный SQL, попробуйте:
Вы можете сделать это менее беспорядочным, добавив методы класса, чтобы скрыть использование
arel_table
, например:... так ...
источник
Vacancy.where("vacancies.project_id = projects.id").exists?
дает либоtrue
илиfalse
.Project.where(true)
этоArgumentError
.Vacancy.where("vacancies.project_id = projects.id").exists?
не будет выполняться - это вызовет ошибку, потому чтоprojects
отношение не будет существовать в запросе (и в приведенном выше примере кода также нет знака вопроса). Поэтому разложение этого на два выражения недопустимо и не работает. В последнее время RailsProject.where(Vacancies.where("vacancies.project_id = projects.id").exists)
выдает предупреждение об устаревании ... Я обновлю вопрос.В Rails 4+, вы можете также использовать включаете или eager_load , чтобы получить тот же ответ:
источник
Думаю, есть более простое решение:
источник
Без особой магии Rails вы можете:
Этот тип условий будет работать во всех версиях Rails, поскольку большая часть работы выполняется непосредственно на стороне БД. Кроме того,
.count
метод цепочки тоже подойдет. Как и раньше, меня обжигали вопросыProject.joins(:vacancies)
. Конечно, есть плюсы и минусы, поскольку он не зависит от БД.источник
Кроме того, можно использовать
EXISTS
с ,SELECT 1
а не выбирать все столбцы изvacancies
таблицы:источник
Ошибка говорит вам, что вакансии - это не столбец в проектах.
Это должно работать
источник
aggregate functions are not allowed in WHERE