我需要一些帮助来实现预加载
I need some help implementing eager loading
最近有人告诉我急切加载及其提高性能的必要性。我已经设法从加载此页面中减少了一些查询,但我怀疑如果我可以正确地预先加载所需的记录,我可以 trim 显着减少它们。
此控制器需要加载以下所有内容以填充视图:
一个学生
学生正在查看的研讨会(class)页面
该研讨会中包含的所有 objective
objective_seminars,objective和研讨会之间的连接table。这包括由教师设置并用于排序 objective 的列 "priority"。
objective_students,另一个加入table。包含学生在 objective.
上的分数的列 "points"
seminar_students,最后加入table。包括学生可以调整的一些设置。
控制器:
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
下面的方法是发生大量重复查询的地方,我认为应该通过预先加载来消除这些查询。此方法为学生提供了六个选项,因此她可以选择一个 objective 来教她的 class 同伴。该方法首先查看学生得分在 75% 到 99% 之间的 objectives。在那个括号内,它们也按 "priority" 排序(从 objective_seminars 加入 table。这个值由老师设置。)如果还有更多空间,则该方法查看objective 学生得分为 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
提前感谢您的任何见解!
@jeff - 关于您的问题,我看不出 @student_scores.find_by(:objective => obj)
以外的地方会有很多查询。
您的 @student_scores
对象已经是一个 ActiveRecord 关系,对吗?因此,您可以在此使用 .where()
或 .select{}
而无需再次访问数据库。 Select 会给你留下一个数组,而不是一个 AR 关系,所以要小心。
this_score = @student_scores.where(objectve: obj)
this_score = @student_scores.select{|score| score.objective == obj}
这些应该有用。
关于你的顶级控制器方法的一些其他建议 - 我没有看到任何守卫或防御编码,所以如果这些对象中的任何一个为零,你的 .order(:blah)
可能会出错。此外,如果它们 return nil,则依赖于它们数据的后续查询可能会出错。我会选择一些 try()
s 或救援。
最后,只是吹毛求疵,但前两行有点难以阅读,因为您可能会错误地将参数解释为应用于包含以及主要对象:
@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)
最近有人告诉我急切加载及其提高性能的必要性。我已经设法从加载此页面中减少了一些查询,但我怀疑如果我可以正确地预先加载所需的记录,我可以 trim 显着减少它们。
此控制器需要加载以下所有内容以填充视图:
一个学生
学生正在查看的研讨会(class)页面
该研讨会中包含的所有 objective
objective_seminars,objective和研讨会之间的连接table。这包括由教师设置并用于排序 objective 的列 "priority"。
objective_students,另一个加入table。包含学生在 objective.
上的分数的列 "points"
seminar_students,最后加入table。包括学生可以调整的一些设置。
控制器:
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
下面的方法是发生大量重复查询的地方,我认为应该通过预先加载来消除这些查询。此方法为学生提供了六个选项,因此她可以选择一个 objective 来教她的 class 同伴。该方法首先查看学生得分在 75% 到 99% 之间的 objectives。在那个括号内,它们也按 "priority" 排序(从 objective_seminars 加入 table。这个值由老师设置。)如果还有更多空间,则该方法查看objective 学生得分为 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
提前感谢您的任何见解!
@jeff - 关于您的问题,我看不出 @student_scores.find_by(:objective => obj)
以外的地方会有很多查询。
您的 @student_scores
对象已经是一个 ActiveRecord 关系,对吗?因此,您可以在此使用 .where()
或 .select{}
而无需再次访问数据库。 Select 会给你留下一个数组,而不是一个 AR 关系,所以要小心。
this_score = @student_scores.where(objectve: obj)
this_score = @student_scores.select{|score| score.objective == obj}
这些应该有用。
关于你的顶级控制器方法的一些其他建议 - 我没有看到任何守卫或防御编码,所以如果这些对象中的任何一个为零,你的 .order(:blah)
可能会出错。此外,如果它们 return nil,则依赖于它们数据的后续查询可能会出错。我会选择一些 try()
s 或救援。
最后,只是吹毛求疵,但前两行有点难以阅读,因为您可能会错误地将参数解释为应用于包含以及主要对象:
@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)