Rails 4:在随机列表上使用偏移会产生意外结果

Rails 4: using offset on a randomized list produces unexpected results

...或者,我只是不明白。

TL;DR - 我得到了完全随机的结果,其中包含重复项等。不知道为什么。

这就是问题所在 - 我正在尝试制作一个 rails 应用程序来生成锦标赛括号。用户可以select在生成括号时是否要随机化团队列表,或者稍后自己分配团队。

如果我选择后一个选项并随着 ID 生成括号,一切都很好。 如果我选择随机化列表,列表本身很好,但稍后在将团队分配给特定比赛时(在循环中),结果完全没有组织且出乎意料(至少对我而言)例如

而不是
第 1 场比赛:第 1 队对第 2 队
第 2 场比赛:第 3 队对第 4 队
等(如正常生成的列表)

我得到一些完全随机的东西,经常重复,比如:
第 1 场比赛:第 1 队对第 1 队
第 2 场比赛:第 1 队对第 9 队

if @seed = 'random'
  @team_list = @tournament.teams.order("RAND()")
else
  @team_list = @tournament.teams.order(:id)
end

这是我分配团队的方式。搞砸一切的事情似乎是 "offset" 部分,我不明白,为什么。

@match.team_a = @team_list.offset((2*match_number)-2).limit(1).first               
@match.team_b = @team_list.offset((2*match_number)-1).limit(1).first

编辑:

请求的@team_list数据样本。那是随机的。

#<ActiveRecord::AssociationRelation 
[#<Team id: 2, team_name: "test_team_a", reputation: 0, created_at: "2016-08-25 09:29:20", updated_at: "2016-08-26 11:08:21", team_leader_id: 2>, 
#<Team id: 9, team_name: "test_team_b", reputation: 0, created_at: "2016-08-30 23:01:17", updated_at: "2016-08-30 23:01:17", team_leader_id: 2>, 
#<Team id: 3, team_name: "test_team_c", reputation: 0, created_at: "2016-08-30 22:59:16", updated_at: "2016-08-30 22:59:16", team_leader_id: 2>, 
#<Team id: 7, team_name: "test_team_d", reputation: 0, created_at: "2016-08-30 23:00:35", updated_at: "2016-08-30 23:00:35", team_leader_id: 2>, 
#<Team id: 5, team_name: "test_team_e", reputation: 0, created_at: "2016-08-30 23:00:07", updated_at: "2016-08-30 23:00:07", team_leader_id: 2>, 
#<Team id: 6, team_name: "test_team_f", reputation: 0, created_at: "2016-08-30 23:00:23", updated_at: "2016-08-30 23:00:23", team_leader_id: 2>, 
#<Team id: 4, team_name: "test_team_g", reputation: 0, created_at: "2016-08-30 22:59:41", updated_at: "2016-08-30 22:59:41", team_leader_id: 2>, 
#<Team id: 8, team_name: "test_team_h", reputation: 0, created_at: "2016-08-30 23:00:46", updated_at: "2016-08-30 23:00:46", team_leader_id: 2>]>

因为你使用了offset。每次更改 match_number 时,您都会执行一个新查询(其中包括对 order_by rand() 的调用)。您需要使 @team_list 成为数据库查询的结果,然后以其他方式组织匹配,这样您就不会重复访问数据库。这也将显着提高性能。

试试

@team_list.each_slice(2) do |team_a, team_b|
end

只是为了进一步阐明上述答案,当您执行 @team_list = @tournaments.teams.order("RAND()") 时,会执行此查询(大概在 SQL 中):

SELECT teams.* FROM teams ORDER BY RAND()

(我知道球队属于锦标赛,但你明白我的意思。)

然后当您执行 @team_list.offset(x).limit(1) 时,会执行以下查询:

SELECT teams.* FROM teams ORDER BY RAND() LIMIT 1 OFFSET x

由于 ActiveRecord 关系,每次你做一件新事,你都在做一个查询。您可以在 rails 控制台中对此进行测试 - 它会告诉您它执行的查询。

编辑:

如果您想按原样使用代码,只需将第一个查询的结果转换为数组即可,不要使用 offset/limit。所以:

@team_list = @tournaments.teams.order("RAND()").to_a
...
@match.team_a = @team_list[2 * match_number - 2]
@match.team_b = @team_list[2 * match_number - 1]

这意味着您将同时加载所有团队(我认为),而不是像 ActiveRecord 关系允许您那样延迟加载它们。