2

有一个带有属性的Website模型 has_many 。Ratingscore

要确定前 10 名获胜者网站,我只需计算并缓存total_score网站记录并在此之后进行排序。

不幸的是,这并不容易——我需要在特定的时间范围内确定获奖者。它可以在奖励时间段之前和之后进行评级。

我该怎么做?分组然后按计数排序(这不起作用 - 见下文)?我猜它有大约 1.000 个网站记录,总共有 100.000 个评分...

Website.left_joins(:ratings).group('ratings.website_id').
        where(ratings: { created_at: Time.parse('2019-01-01')..Time.parse('2019-06-01') }).
        order('count(ratings.score) desc').
        limit(10)

或者创建类似那个时期的快照?

任何建议都会很棒!

4

1 回答 1

2

如果没有连接/子查询:

即,如果您只检索前 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.*意味着对象的其他属性(如idscoreratings都是 all nil,只有除了website_id

如果有连接/子查询:

编辑:下面的 TODO 还没有完全工作;可能需要帮助。无法找到/解决保留website_id子查询外部排序的方法。这段时间很忙。

如果您要迭代每条website记录,或者要检索数千website条记录,以防止 N+1 查询。

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)由于 a ,急切加载似乎不起作用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
于 2019-06-11T10:00:32.833 回答