我在这里有两种方法,distinct_question_ids 和 @correct_on_first 尝试。目标是向用户展示有多少不同的多项选择题已被正确回答。
第二个将让我知道在第一次尝试中正确回答了多少这些不同的 MCQ。 (用户可以多次尝试 MCQ)
现在,当用户回答数千个问题并有数千个用户答案时,显示其性能的页面需要 30 秒到 1 分钟才能加载。我相信这是由于 .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
最佳答案
.pluck
返回值数组,而不是 ActiveRecord::Relation。
因此,当您执行 distinct_question_ids.select
时,您不是在调用 ActiveRecord 的 select
,而是调用 Array 的 select
。在该选择中,您将针对您刚刚选择的每个 id 对 @user 发出一个全新的查询——包括在选择中被拒绝的那些。
您可以创建一个名为 distinct_questions
的查询,该查询返回一个关系(没有勇气!),然后从中构建 correct_on_first_attempt
,我认为您将避免 N+1 查询。
沿着这些路线的东西:
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 来获得类似的东西。但是...似乎您的某些项目要求取决于该序列号,因此如果您还没有它,我建议您添加它。
附:对于 N+1 查询,请使用 gem bullet。这是在点。
关于ruby-on-rails - .select 和或 .where 是否负责导致 Rails 中的 N+1 个查询?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54499631/