Reputation: 203
Say I have a Teacher model that has_many students through class_listings consisting of teacher_id and student_id:
class Teacher < ActiveRecord::Base
has_many :class_listings
has_many :students, through: :class_listings
end
class ClassListing < ActiveRecord::Base
belongs_to :teacher
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :class_listings
has_many :teachers, through: :class_listings
end
and there exists:
How would I query for a teacher having student 1 and student 2?
I've tried
Teacher.joins(:class_listings).where(class_listings: {student_id: [1,2]})
but that returns teachers with students 1 or 2 (i.e. teacher 1, 2, and 3) as opposed to teachers with students 1 and 2 (i.e. teacher 3).
Upvotes: 1
Views: 1970
Reputation: 1051
Assuming that your class listings are uniq, you may also do it like that:
student_ids = [1, 2]
Teacher.where(
id: Rel.select(:teacher_id).group(:teacher_id).where(student_id: student_ids)
.having("count(student_id) = ?", student_ids.count)
)
SQL is:
SELECT "teachers".* FROM "teachers" WHERE "teachers"."id" IN (
SELECT "rels"."teacher_id" FROM "rels" WHERE "rels"."student_id" IN (1, 2)
GROUP BY "rels"."teacher_id" HAVING count(student_id) = 2
)
PS: I use Rel
model instead of ClassListing
, sorry
PPS: I like @histocrat's solution too.
Upvotes: 4
Reputation: 2381
This is awkward. Here's one way without writing straight SQL:
Teacher.where(id: ClassListing.where(student_id: 1).select(:teacher_id))
.where(id: ClassListing.where(student_id: 2).select(:teacher_id))
This is composed of multiple where clauses, but it will only actually make one SQL query, something like Select * from teachers where id in (Select teacher_id from ClassListing where student_id = 1) AND id in (Select teacher_id from ClassListing where student_id = 2)
.
Upvotes: 4