【问题标题】:Rails 3 - Turning multiple counts into a single query - OrderedHashRails 3 - 将多个计数转换为单个查询 - OrderedHash
【发布时间】:2023-03-28 04:51:01
【问题描述】:

我有一个初始化方法,它做了一件愚蠢的事情。我需要将其优化为一个查询,但我的 SQL 技能目前令我失望。我设想过使用 GROUP BY 和 UNION 以及各种各样的东西,但我只是让自己更加困惑。我将此遗赠给社区以提供一些见解:

Class Stats
  # Turn these three queries into one query that we can then
  # load into the three different instance variables
  def initialize(question)
    # Integer = total number of answers for this question
    @total = total_answers(question.id)

    # Hash keyed by 0 (incorrect answers) and 1 (correct answers)
    @stats_total = load_stats_total(question.id) if @total > 0

    # Hash keyed by answer_id with values = total number of answers
    @stats_answers = load_stats_answers(question.id) if @total > 0
  end

  # Returns an int = the total number of answer attempts for
  # this question (right + wrong user_answers)
  # Excludes anonymous users
  def total_answers(question_id)
    UserAnswer.count(:conditions => ['u.anonymous = 0 AND q.id = ?', question_id], :joins => 'JOIN answers a ON user_answers.answer_id = a.id JOIN questions q ON q.id = a.question_id JOIN users u ON u.id = user_answers.user_id')
  end

  # Returns an OrderedHash =
  # {"0" => number of wrong user_answers for this question,
  #  "1" => number of correct user_answers for this question}
  # Excludes anonymous users
  def load_stats_total(question_id)
    UserAnswer.count(:conditions => ['u.anonymous = 0 AND q.id = ?', question_id], :joins => 'JOIN answers a ON user_answers.answer_id = a.id JOIN questions q ON q.id = a.question_id JOIN users u ON u.id = user_answers.user_id', :group => 'a.correct')
  end

  # Returns an OrderedHash =
  # {
  #  some_answer_id => total number of user_answers for this answer,
  #  some_other_answer_id => total number of user_answers for this answer
  #  ...
  # }
  # Excludes anonymous users
  def load_stats_answers(question_id)
    UserAnswer.count(:conditions => ['u.anonymous = 0 AND q.id = ?', question_id], :joins => 'JOIN answers a ON user_answers.answer_id = a.id JOIN questions q ON q.id = a.question_id JOIN users u ON u.id = user_answers.user_id', :group => 'a.id')
  end
end

如果有人有任何想法,他们将不胜感激! 谢谢。

【问题讨论】:

    标签: mysql ruby-on-rails optimization activerecord count


    【解决方案1】:

    我不认为你可以在一个查询中干净地做到这一点。 至少不用写纯sql就不行。

    但是让我们尝试在 ActiveRecord 中找到一个不错的解决方案

    首先,让我们尝试删除一些sql

    UserAnswer.count(:conditions => ['u.anonymous = 0 AND q.id = ?', question_id], :joins => 'JOIN answers a ON user_answers.answer_id = a.id JOIN questions q ON q.id = a.question_id JOIN users u ON u.id = user_answers.user_id')
    

    可以改写

    UserAnswer.joins(:user).where(:users => {:anonymous => false})\
      .joins(:answer => :question).where(:questions => {:id => question_id})\
      .count
    

    让我们把这个作用域保存为一个神奇的私有方法magic_scope

    你现在的方法变成了

    def total_answers(question_id)
      magic_scope(question_id).count
    end
    
    def load_stats_total(question_id)
      magic_scope(question_id).count(:group => "answers.correct")
    end
    
    def load_stats_answers(question_id)
      magic_scope(question_id).count(:group => "answers.id")
    end
    

    值得注意的是,total_answers 方法当然可以通过对任一 load_stats_* 方法的结果求和得出。

    如果 ActiveRecord 更聪明一点,我们可以做

    def all_the_magic(question_id)
      magic_scope(question_id).count(:group => ["answers.correct", "answers.id"])
    end
    

    这将为我们提供一次查询所需的所有数据。

    但据我所知,目前还不可能。

    但我希望这能让你更接近。

    【讨论】:

      猜你喜欢
      • 2011-04-16
      • 2014-03-02
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 2020-12-10
      • 1970-01-01
      • 2017-03-23
      • 1970-01-01
      相关资源
      最近更新 更多