【问题标题】:How can I make a outer join in a complex Rails has_many scope?如何在复杂的 Rails has_many 范围内进行外部连接?
【发布时间】:2018-06-01 07:06:32
【问题描述】:

我的目标是创建一个可以查看Member 对象的所有已完成课程的范围。

一个Course 由许多Sections 组成。每个Section 有一个Quiz。并且每个Quiz 都必须设置为complete 才能使Course 完整。

例如:

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  

但它不应该返回一个[ ] 值。例如我有这个数据:

->  Course{id: 1}
    -> Section{id: 1, course_id: 1}
        -> Quiz{section_id: 1, member_id: 1, completed: true}

本课程共有一节。该部分可能有数百个与其他成员相关的测验,但对于这个特定的成员,他的测验已完成。

我认为这与此 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" = $1 
 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]]

【问题讨论】:

    标签: ruby-on-rails postgresql join scope psql


    【解决方案1】:
    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 组。

    【讨论】:

    • 这真是不可思议,谢谢。但它并不完全有效,因为即使所有测验都已完成,它也总是返回 [ ] 值。我将在上面更新一个示例。
    • 糟糕,我想通了(这与我没有告诉你的其他属性不对齐有关)。非常感谢你的帮助!你完全帮我解决了这个问题。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 2014-03-18
    • 2012-02-13
    • 1970-01-01
    • 2011-11-05
    • 2014-10-10
    • 2016-08-23
    相关资源
    最近更新 更多