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 关系允许您那样延迟加载它们。
...或者,我只是不明白。
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 关系允许您那样延迟加载它们。