【问题标题】:I need some help implementing eager loading我需要一些帮助来实现急切加载
【发布时间】:2017-08-07 17:24:52
【问题描述】:

我最近被告知急切加载及其在提高性能方面的必要性。我已经设法从加载此页面中删除了一些查询,但我怀疑如果我可以正确地急切加载所需的记录,我可以进一步减少它们。

此控制器需要加载以下所有内容来填充视图:

  • 一个学生

  • 学生正在查看的研讨会(班级)页面

  • 该研讨会包含的所有目标

  • objective_seminars,目标和研讨会之间的连接表。这包括由教师设置并用于对目标进行排序的“优先级”列。

  • objective_students,另一个连接表。包括学生在该目标上的分数的“分”列。

  • seminar_students,最后一个连接表。包括一些学生可以调整的设置。

控制器:

def student_view
    @student = Student.includes(:objective_students).find(params[:student])
    @seminar = Seminar.includes(:objective_seminars).find(params[:id])
    @oss = @seminar.objective_seminars.includes(:objective).order(:priority)
    @objectives = @seminar.objectives.order(:name)
    objective_ids = @objectives.map(&:id)
    @student_scores = @student.objective_students.where(:objective_id => objective_ids)
    @ss = @student.seminar_students.find_by(:seminar => @seminar)
    @teacher = @seminar.user

    @teach_options = teach_options(@student, @seminar, 5)
    @learn_options = learn_options(@student, @seminar, 5)
end

下面的方法是发生大量重复查询的地方,我认为这些重复查询应该通过预先加载来消除。这种方法为学生提供了六个选项,因此她可以选择一个目标来教她的同学。该方法首先查看学生得分在 75% 到 99% 之间的目标。在该括号内,它们也按“优先级”排序(来自objective_seminars 连接表。此值由老师设置。)如果还有更多空间,则该方法会查看学生得分为100%的目标,排序按优先级。 (learn_options 方法实际上和这个方法是一样的,只是括号号不同。)

teach_options 方法:

def teach_options(student, seminar, list_limit)
        teach_opt_array = []
        [[70,99],[100,100]].each do |n|
            @oss.each do |os|
                obj = os.objective
                this_score = @student_scores.find_by(:objective => obj)
                if this_score
                    this_points = this_score.points
                    teach_opt_array.push(obj) if (this_points >= n[0] && this_points <= n[1])
                end
            end
            break if teach_opt_array.length > list_limit
        end
        return teach_opt_array
    end

提前感谢您的任何见解!

【问题讨论】:

    标签: mysql ruby-on-rails eager-loading


    【解决方案1】:

    @jeff - 关于您的问题,我看不出在@student_scores.find_by(:objective =&gt; obj) 之外会发生很多查询。 您的 @student_scores 对象已经是 ActiveRecord 关系,对吗?因此,您可以在此使用 .where().select{} 而无需再次访问数据库。 Select 会给你一个数组,而不是一个 AR 关系,所以要小心。

    this_score = @student_scores.where(objectve: obj)
    this_score = @student_scores.select{|score| score.objective == obj}
    

    那些应该工作。

    只是关于您的顶级控制器方法的一些其他建议 - 我没有看到任何守卫或防御性编码,因此如果这些对象中的任何一个为 nil,您的 .order(:blah) 可能会出错。此外,如果它们返回 nil,则依赖于其数据的后续查询可能会出错。我会选择一些try()s 或救援。

    最后,只是有点挑剔,但前两行有点难以阅读,因为您可能会错误地将参数解释为应用于包含以及主要对象:

    @student = Student.includes(:objective_students).find(params[:student])
    @seminar = Seminar.includes(:objective_seminars).find(params[:id])
    

    我会将 find 与您的主要对象放在一起,然后是包含:

    @student = Student.find(params[:student]).includes(:objective_students)
    @seminar = Seminar.find(params[:id]).includes(:objective_seminars)
    

    【讨论】:

    • 你是说“where”不会向数据库发送另一个命中,但“find_by”会?如果是这样,那是有道理的。此外,链末尾带有“包含”的语法是我首先尝试的方式。它以这种方式引发了错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 2022-07-12
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多