【问题标题】:Rails: more efficient way to export many-to-many tables to excelRails:将多对多表导出到 Excel 的更有效方法
【发布时间】:2014-05-03 18:07:44
【问题描述】:

我有以下型号:

class Student
  attr_accessible :name
  has_many :courses, through: :course_students
  has_many :course_students
end

class Course
  attr_accessible :name
  has_many :students, through: :course_students
  has_many :course_students
end

class CourseStudent
  attr_accessible :grade

  belongs_to :course
  belongs_to :student
end

现在,我正在尝试生成一个 Excel 表格,其中学生为行,课程为列,交叉点将显示学生在该课程中的成绩。

到目前为止,我处理的是一小群学生,所以下面的算法是可以的:

<table>
  ...
  <tbody>
    <% @students..includes(:courses)each do |student| %>
      <tr>
        ...
        <% student.courses.includes(:course_students).each do |course| %>
          <td><%= course.course_students.find_by_student_id(student.id).try(:grade) || '-' %></td>
        <% end %>
      </tr>  
    <% end %>
  </tbody>
</table>

如您所见,我一直在尝试在加载数据时包含课程和 course_students,但仍然收到大量查询。我知道这是一个经典的 N+1 查询问题(更像是 NxM+N+M+1),但通常的方法不起作用。

我希望在更少的查询中提取我需要的所有数据。有人有想法吗?

【问题讨论】:

    标签: sql ruby-on-rails has-many-through


    【解决方案1】:

    首先,您应该按照某种标准化的排序顺序收集所有课程(我随意使用name,但它可以是任何东西):

    <% @courses = Course.order(:name) %>
    

    您还应该生成一个标题行来显示课程列:

    <tr>
      ...
      <% @courses.each do |course| %>
        <td><%= course.name %></td>
      <% end %>
    </tr>
    

    现在,您需要遍历每个学生(急切加载 :course_student 连接模型以避免 N+1)

    <% @students.includes(:course_students).each do |student| %>
      <tr>
        ...
        <% @courses.each do |course| %>
          <td><%= student.course_students.detect do |cs| 
                    cs.course_id == course.id
                  end.try(:grade) || '-' %></td>
        <% end %>
      </tr>  
    <% end %>
    

    您可以通过避免调用detect 并为每个学生构建一个成绩散列来进一步优化这一点:

        ...
        <% grades = student.course_students.each_with_object(Hash.new('-')) do |cs, hash| 
             hash[cs.course_id] = cs.grade
           end %>
        <% @courses.each do |course| %>
          <td><%= grades[course.id] %></td>
        <% end %>
        ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-27
      • 2016-10-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      相关资源
      最近更新 更多