Rails 3 - превращение нескольких подсчетов в один запрос - OrderedHash - PullRequest
3 голосов
/ 14 июня 2011

У меня есть метод инициализатора, который делает глупость. Мне нужно оптимизировать его до одного запроса, но мои навыки работы с 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

Если у кого-то есть мысли, они будут очень благодарны! Спасибо.

1 Ответ

3 голосов
/ 14 июня 2011

Я не думаю, что вы можете сделать это чисто в одном запросе.По крайней мере, без написания чистого 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

, что дало бынам все данные, которые нам нужны, чтобы сделать это в одном запросе.

, но, насколько я знаю, в настоящее время это невозможно.

Но я надеюсь, что это приближает вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...