如何在复杂的 Rails has_many 范围内进行外部连接?
How can I make a outer join in a complex Rails has_many scope?
我的目标是建立一个可以看到 Member
对象的所有已完成课程的范围。
一个Course
是由很多Sections
组成的。每个 Section
有一个 Quiz
。为了使 Course
完整,每个 Quiz
都必须设置为 complete
。
例如:
Course
Section A -> Quiz A ( Complete )
Section B -> Quiz B ( Complete )
Section C -> Quiz C ( Complete )
这是我编写这种作用域的最佳尝试:
# Member.rb
has_many :completed_courses, -> {
joins(:quizzes, :sections)
.where(quizzes: {completed: true})
.group('members.id HAVING count(sections.quizzes.id) = count(sections.id)')
}, through: :course_members, source: :course
我遗漏的部分是 count(sections.quizzes.id)
这部分实际上不是 SQL。我不完全确定这会被称为哪种 JOIN
,但我需要一些方法来 count
属于课程的已完成测验,并将该数字与多少部分进行比较。如果它们相等,则表示所有测验都已完成。
公平地说,只要知道这种 JOIN 的名称就可能让我走上正确的方向。
更新
我尝试使用@jamesdevar 的回复:
has_many :completed_courses, -> {
joins(:sections)
.joins('LEFT JOIN quizzes ON quizzes.section_id = sections.id')
.having('COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)')
.group('courses.id')
}, through: :course_members, source: :course
但它 returns 一个 [ ]
不应该的值。例如我有这个数据:
-> Course{id: 1}
-> Section{id: 1, course_id: 1}
-> Quiz{section_id: 1, member_id: 1, completed: true}
课程一共有一个Section。该部分可能有数百个来自其他成员的与之相关的测验,但对于这个特定的成员,他的测验已经完成。
我认为这与此 SQL 比较不是针对单个成员的唯一性有关。
.having('COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)')
实际生成的SQL是这样的:
SELECT "courses".* FROM "courses"
INNER JOIN "sections" ON "sections"."course_id" = "courses"."id"
INNER JOIN "course_members" ON "courses"."id" = "course_members"."course_id"
LEFT JOIN quizzes ON quizzes.section_id = sections.id
WHERE "course_members"."member_id" =
GROUP BY courses.id
HAVING COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)
ORDER BY "courses"."title"
ASC [["member_id", 1121230]]
has_many :completed_courses, -> {
joins(:sections)
.joins('LEFT JOIN quizzes ON quizzes.section_id = sections.id')
.having('COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)')
.group('courses.id')
}, through: :course_members, source: :course
在这种情况下,having
将在每个部分完成测验后按条件过滤每个 cources.id
组。
我的目标是建立一个可以看到 Member
对象的所有已完成课程的范围。
一个Course
是由很多Sections
组成的。每个 Section
有一个 Quiz
。为了使 Course
完整,每个 Quiz
都必须设置为 complete
。
例如:
Course
Section A -> Quiz A ( Complete )
Section B -> Quiz B ( Complete )
Section C -> Quiz C ( Complete )
这是我编写这种作用域的最佳尝试:
# Member.rb
has_many :completed_courses, -> {
joins(:quizzes, :sections)
.where(quizzes: {completed: true})
.group('members.id HAVING count(sections.quizzes.id) = count(sections.id)')
}, through: :course_members, source: :course
我遗漏的部分是 count(sections.quizzes.id)
这部分实际上不是 SQL。我不完全确定这会被称为哪种 JOIN
,但我需要一些方法来 count
属于课程的已完成测验,并将该数字与多少部分进行比较。如果它们相等,则表示所有测验都已完成。
公平地说,只要知道这种 JOIN 的名称就可能让我走上正确的方向。
更新
我尝试使用@jamesdevar 的回复:
has_many :completed_courses, -> {
joins(:sections)
.joins('LEFT JOIN quizzes ON quizzes.section_id = sections.id')
.having('COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)')
.group('courses.id')
}, through: :course_members, source: :course
但它 returns 一个 [ ]
不应该的值。例如我有这个数据:
-> Course{id: 1}
-> Section{id: 1, course_id: 1}
-> Quiz{section_id: 1, member_id: 1, completed: true}
课程一共有一个Section。该部分可能有数百个来自其他成员的与之相关的测验,但对于这个特定的成员,他的测验已经完成。
我认为这与此 SQL 比较不是针对单个成员的唯一性有关。
.having('COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)')
实际生成的SQL是这样的:
SELECT "courses".* FROM "courses"
INNER JOIN "sections" ON "sections"."course_id" = "courses"."id"
INNER JOIN "course_members" ON "courses"."id" = "course_members"."course_id"
LEFT JOIN quizzes ON quizzes.section_id = sections.id
WHERE "course_members"."member_id" =
GROUP BY courses.id
HAVING COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)
ORDER BY "courses"."title"
ASC [["member_id", 1121230]]
has_many :completed_courses, -> {
joins(:sections)
.joins('LEFT JOIN quizzes ON quizzes.section_id = sections.id')
.having('COUNT(sections.id) = SUM(CASE WHEN quizzes.completed = true THEN 1 ELSE 0 END)')
.group('courses.id')
}, through: :course_members, source: :course
在这种情况下,having
将在每个部分完成测验后按条件过滤每个 cources.id
组。