Rails 3 - 将多个计数转换为单个查询 - OrderedHash

发布于 2024-11-15 09:30:02 字数 2142 浏览 1 评论 0原文

我有一个初始化方法,它做了一件愚蠢的事情。我需要将其优化为一个查询,但目前我的 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

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

I've got an initializer method that does a silly thing. I need to optimize it down to one query, but my SQL skills are failing me at the moment. I've conceived of using GROUP BY and UNION and all sorts of things, but I just made myself more confused. I bequeath this to the community to shed some insight:

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

If anyone has any thoughts, they'd be greatly appreciated!
Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

放飞的风筝 2024-11-22 09:30:02

我认为您无法在一个查询中干净地完成此操作。
至少在不编写纯sql的情况下是这样。

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

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

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

sql让我们将此范围保存为一个神奇的私有方法 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

这一点,这将为我们提供在一次查询中执行此操作所需的所有数据。

但据我所知,目前这是不可能的。

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

I don't think you can do this cleanly in one query.
At least not without writing pure sql.

But lets try and find a nice solution in ActiveRecord

First of all, let's try to remove some of the 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')

can be rewritten

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

lets just save this scope as a magic private method magic_scope

your current methods become

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

notably, of course, the total_answers method can be derived from summing up the results from either of the load_stats_* methods.

If ActiveRecord was a bit more clever we could do

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

which would give us all the data we needed to do it in one query.

but as far as I'm aware that's not currently possible.

But I hope this gets you nearer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文