我需要一些帮助来实现预先加载

时间:2017-08-07 17:24:53

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

我最近得到了热切的加载以及提高性能的必要性。我设法从加载此页面中删除了一些查询,但我怀疑如果我能够正确地加载所需的记录,我可以将它们显着减少。

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

  • 学生

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

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

  • objective_seminars,目标和研讨会之间的联接表。这包括列"优先级"这是由老师设定的,用于订购目标。

  • objective_students,另一个连接表。包括一个列"点"学生在该目标上的得分。

  • semin_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%之间的目标。在该括号内,它们也按" priority"排序。 (来自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

提前感谢您的任何见解!

1 个答案:

答案 0 :(得分:0)

@jeff - 关于你的问题,我没有看到@student_scores.find_by(:objective => obj)之外会发生很多疑问。 您的@student_scores对象已经是ActiveRecord关系,对吗?因此,您可以对此使用.where(),或.select{},而无需再次访问数据库。选择会留下一个数组,而不是AR关系,所以要小心。

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

那些应该有效。

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

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

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

我将查找与您的主要对象,然后是包括:

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