Если без соединений / подзапросов:
т.е. если вы запрашиваете только топ-10 (только несколько) веб-сайтов, а не тысячи website
записей, или что вы не собираетесь повторять каждый из тысяч массивов.
ratings = Rating.where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01')
).group(
:website_id
).order(
'sum(score) desc'
).select(
:website_id
)
top_10_winner_websites = ratings.take(10).map(&:website)
# above is ordered by sum(score) desc
# so,
# top_10_winner_websites[0] has the highest sum score, while
# top_10_winner_websites[-1] has the lowest sum score amongst the 10
Внимание: обратите внимание, что приведенный выше запрос только "выбирает" ratings.website_id
, а не ratings.*
, что будет означать, что другие атрибуты (например, id
и score
) из ratings
объекты будут все nil
, только за исключением website_id
Если с объединениями / подзапросами:
Редактировать: TODO ниже еще не работает; может понадобиться помощь Не удалось найти / решить способ сохранения порядка website_id
вне подзапроса. В данный момент занят.
Для предотвращения N + 1 запросов, если вы собираетесь повторять каждую website
запись, или если вы собираетесь извлекать тысячи website
записей.
top_10_winner_websites = Website.where(
id: Rating.where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01')
).group(
:website_id
).order(
'sum(ratings.score) desc'
).select(
'website_id r'
)
).limit(10).order('r.website_id asc')
Текущий обходной путь (для незавершенного подзапроса выше):
в качестве обходного пути для решения «сохранения порядка» вне подзапроса, а также для предотвращения N + 1 запросов:
ratings = Rating.where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01')
).group(
:website_id
).order(
'sum(ratings.score) desc'
).select(
:website_id
)
top_10_winner_website_ids = ratings.take(10).map(&:website_id)
top_10_winner_websites = Website.where(
id: top_10_winner_website_ids
).sort_by do |website|
top_10_winner_website_ids.index(website.id)
end
Редактировать: По расширенному запросу вы можете проверить и получить рейтинг сайта:
website_to_check = Website.find(1)
index = top_10_winner_websites.index{|winner_website| winner_website.id == website_to_check.id }
if index.nil?
puts 'Website was not part of the top 10 winners in this time period'
else
puts "Website rank for this time period was: #{index + 1}"
end
^ Если вам для этого нужна чистая проверка ранга SQL, я не слишком уверен, как это реализовать.
Редактировать: на расширенный запрос, чтобы иметь дополнительные "условия" для Website
записей:
... вы все еще можете использовать joins(:website)
, но это не мешает N + 1 запросам по сравнению с eager_load(:website)
или includes(:website)
, но нетерпеливая загрузка не работает из-за PG::GroupingError
, но вы все еще можете предотвратить N + 1 запросов, используя обходной путь выше. Смотрите полный пример ниже:
ratings = Rating.joins(:website).where(
created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01'),
websites: {
some_website_attribute_1: true, # UPDATE THIS
some_website_attribute_2: 'foobar' # UPDATE THIS
}
).group(
:website_id
).order(
'sum(ratings.score) desc'
).select(
:website_id
)
top_10_winner_website_ids = ratings.take(10).map(&:website_id)
top_10_winner_websites = Website.where(
id: top_10_winner_website_ids
).sort_by do |website|
top_10_winner_website_ids.index(website.id)
end