Запрос ActiveRecord для модели, на которую нет ссылок в другой модели

У меня есть что-то похожее на следующие 2 модели:

class Person < ActiveRecord::Base
  has_one :student
end
class Student < ActiveRecord::Base
  belongs_to :person
end

Я знаю, что могу найти всех студентов с:

Person.joins(:student)

Как мне найти всех людей, которые не являются студентами? Есть ли способ сделать это с помощью ActiveRecord и не писать SQL-запрос?




Ответы (2)


Используйте это утверждение:

Person.includes(:student).
       where(:students => {:id => nil})

or

Person.joins("LEFT JOIN students ON students.person_id = people.id").
       where(:students => {:id => nil})

Я предпочитаю последний, потому что он не выбирает ненужные столбцы.

person Georg Ledermann    schedule 16.04.2015
comment
Ты прав. Мне нравится 2-е утверждение. Я не знаю, какова производительность подзапроса в PostgreSQL (база данных, которую мы используем в производстве). Я надеялся, что это будет оптимизировано, чтобы быть очень эффективным. - person at.; 16.04.2015

Поскольку вы используете рельсы 4, вы можете сделать это следующим образом:

Person.where.not(:id => Student.select(:person_id))
person IngoAlbers    schedule 13.04.2015
comment
Запрос, который он выдает, выглядит хорошо, но не возвращает никаких строк (когда должен): SELECT "people".* FROM "people" WHERE ("people"."id" NOT IN (SELECT "students"."person_id" FROM "students")). У меня есть много объектов Person в базе данных, чьи id не находятся в столбце person_id какой-либо записи students. Интересно, что если я уберу not из запроса, я получу правильных людей, которые являются студентами. Я просто не понимаю обратного, когда есть not. - person at.; 13.04.2015
comment
Причина, по которой у меня это не сработало, заключается в том, что у меня было nil person_id полей в Student. Вот такой запрос у меня работает: Person.where.not(id: Student.select(:person_id).where.not(person_id: nil)) - person at.; 13.04.2015
comment
Осторожно: это два запроса! И Student.select(:person_id) извлекает много данных из базы данных, если таблица Student большая. Смотрите мой ответ для лучшего решения. - person Georg Ledermann; 16.04.2015
comment
@GeorgLedermann, с точки зрения Rails, это было не 2 запроса, в базу данных был отправлен только 1 SQL-запрос. Если вы имеете в виду, что он создает подзапрос внутри запроса, это правильно. Хотя я надеюсь, что база данных будет умной в отношении выполнения. - person at.; 16.04.2015
comment
@в. Вы правы, в Rails 4 это один оператор SQL, включающий подзапрос. В большинстве случаев соединение выполняется быстрее, чем подзапрос. Оптимизатор PostgreSQL может переписать запрос, так что, возможно, это не имеет значения. Чтобы убедиться, вы должны использовать explain для получения плана запроса. - person Georg Ledermann; 17.04.2015