是。选择和或。凡负责铁轨导致N + 1个查询?

时间:2019-02-03 03:10:44

标签: ruby-on-rails ruby

我这里有两个方法,distinct_question_ids和@correct_on_first尝试。我们的目标是向用户展示多少个不同的选择题已经回答是正确的。

第二个让我知道在第一次尝试中正确回答了多少个不同的MCQ。 (用户可以多次尝试MCQ)

现在,当用户回答数千个问题并拥有数千个用户答案时,显示其性能的页面将花费30秒到一分钟的时间来加载。而且我相信这是由于.select方法引起的,但是我不知道如何在不使用.select的情况下替换.select,因为它就像.each

一样循环

是否有不引起N + 1?

的任何方法
distinct_question_ids = @user.user_answers.includes(:multiple_choice_question).
  where(is_correct_answer: true).
  distinct.pluck(:multiple_choice_question_id)

@correct_on_first_attempt = distinct_question_ids.select { |qid|
  @user.user_answers.
    where(multiple_choice_question_id: qid).first.is_correct_answer
}.count

1 个答案:

答案 0 :(得分:3)

.pluck返回一个值数组,而不是ActiveRecord :: Relation。

因此,当您执行distinct_question_ids.select时,不是调用ActiveRecord的select,而是调用Array的select。在该选择项中,您将针对您刚刚采出的每个ID(包括在选择项中被拒绝的ID)针对@user发出全新的查询。

您可以创建一个名为distinct_questions的查询,该查询返回一个关系(不要采空!),然后在此基础上构建correct_on_first_attempt,我认为您将避免N + 1个查询。 / p>

遵循以下原则:

class UserAnswer < ActiveRecord::Base
   scope :distinct_correct, -> { includes(:multiple_choice_question)
                                   .where(is_correct_answer: true).distinct }

   scope :first_attempt_correct, -> { distinct_correct
                                        .first.is_correct_answer }
end

class User < ActiveRecord::Base
  def good_guess_count
    @correct_on_first_attempt = @user.user_answers.distinct_correct.first_attempt_correct.count
  end
end

您需要确保.first实际上是第一次尝试,可能是通过id或created_at排序的。

顺便说一句,如果您在UserAnswer中明确跟踪尝试次数,则可以真正地加强它:

class UserAnswer < ActiveRecord::Base
  scope :correct, -> { where(is_correct_answer: true) }
  scope :first_attempt, -> { where(attempt: 1) }
end

class User < ActiveRecord::Base
  def lucky_guess_count
    @correct_on_first_attempt = @user.user_answers.includes(:multiple_choice_question)
                                     .correct.first_attempt.count
  end
end

如果您的架构中没有尝试编号,则可以使用.order和.group获得类似的内容。但是...看来您的某些项目要求取决于该序列号,因此,如果您还没有序列号,我建议您添加它。

ps。要解决N + 1个查询,请使用gem bullet。准点。