Rails 3 Sort Scope по самонакопленному счетчику

Итак, у меня есть таблица результатов для турнира, где может состояться несколько матчей с одним и тем же игроком.

Таким образом, некоторые примеры данных могут быть:

Результаты

Player_ID | Match_ID|Win|Elapsed_Time|etc..
1         |    1    |T  | 1:00       |etc..
2         |    1    |F  | 1:00       |etc..
1         |    2    |T  | 3:00       |etc..
3         |    2    |F  | 3:00       |etc..

Я хочу подготовить область, которая будет подсчитывать поля Win, для которых установлено значение True для каждого игрока, и группировать по этому полю счета.

Таким образом, псевдокод будет чем-то вроде...

Scope :most_wins, :all, :order_by => "Количество (игрок выигрывает)"

Возможно ли это, или мне следует переосмыслить структуру моей базы данных?


person Noz    schedule 18.05.2012    source источник


Ответы (1)


Прежде всего: Именованные области действия мертвы

Я предлагаю вам сделать следующее:

class Players < ActiveRecord::Base
  ....
  def self.with_win_count
    #inner join if you want to filter out those who doesn't have any match, otherwise is should be left outer 
    joins("inner join (select Player_ID, count(Match_ID) as win_count from Results where Win = true group by Player_ID) t on (t.Player_ID = Player.ID)")
  end
end

#somewhere in controller you would do
best_players = Player.with_win_count.where("win_count > ?", [10]).order("win_count DESC")

Если вы хотите, чтобы это было в таблице результатов:

class Results < ActiveRecord::Base
  ....
  def self.with_win_count
    joins("inner join (select Player_ID, count(Match_ID) as win_count from Results where Win = true group by Player_ID) t on (t.Player_ID = Results.Player_ID)")
  end
end

это сработает, но я нахожу это немного некрасивым.

И если вам нужен условный подсчет во вложенном запросе, простым способом будет:

class Players < ActiveRecord::Base
  ....
  def self.with_match_count(conditions)
    #inner join if you want to filter out those who doesn't have any match, otherwise is should be left outer 
    joins("inner join (select Player_ID, count(Match_ID) as match_count from Results where #{conditions} group by Player_ID) t on (t.Player_ID = Player.ID)")
  end
end
some_players = Player.with_match_count("Win = true").where("match_count > ?", [10]).order("match_count DESC")

!!! Обратите внимание, что он подвержен SQL-инъекциям, если аргумент conditions будет построен непосредственно из пользовательского ввода.

person forker    schedule 18.05.2012
comment
HI Forker, спасибо, что рассказали мне об областях в Rails 3. Предоставленный вами код почти работает, но мне пришлось изменить def with_win_count на self.with_win_count, иначе он выдал бы метод undefined для ошибки класса. - person Noz; 22.05.2012
comment
Есть ли способ сохранить это в рамках только таблицы результатов? Причина, по которой я спрашиваю, заключается в том, что я использую это в динамическом запросе, который создается, и есть дополнительные поля из таблицы результатов, которые я хотел бы отфильтровать. Что-то вроде: query = Player.with_win_count.where(..........) query = query.where(results_field = ....) if condition конечно, таблица Player является родителем результатов - person Noz; 26.05.2012
comment
Я попытался просто добавить дополнительные поля результатов, которые я хотел выбрать в with_win_count и группе, но это, похоже, не работает правильно - person Noz; 26.05.2012
comment
Хороший выбор! Тем не менее, я думаю, что понял ваш вопрос, и последний добавленный фрагмент должен помочь. Таким образом, с помощью with_match_count вы сможете подсчитывать количество побед, поражений и других матчей. - person forker; 26.05.2012
comment
Эй, Форкер, еще раз спасибо за вашу помощь, которая отлично сработала для меня. Я думаю, что предпочитаю идею указания условного счетчика в контроллере в соответствии с вашим первым примером для лучшего повторного использования, а также тот факт, что мы можем защитить от SQL-инъекций. - person Noz; 28.05.2012