Rails 3:给定一名球员,找出他们与每个对手配对的次数
Rails 3: Given a player, find the number of times they have been paired with each opponent
我正在使用 Rails 3.2。我的模式相当复杂但很简单,我有个人电脑(代表玩家)、游戏和项目,因此:
class Game < ActiveRecord::Base
has_many :projects
class Pc < ActiveRecord::Base
has_many :projects
class Project < ActiveRecord::Base
has_many :games
belongs_to :pc
注意:GameProject 还有一个 table 以及多对多关联。
我试图找到一个查询(ActiveRecord 优于完全原始的 SQL)这样,给定一台 Pc,returns 是其他每台 Pc 玩过的游戏数量反对他们。如果同一游戏中有两个 Pc 的项目,则一个 Pc 与另一个 Pc 对战。示例情况是:
+------+---------+----+
| Game | Project | Pc |
+------+---------+----+
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 1 | 4 | 2 |
| 1 | 5 | 2 |
| 2 | 6 | 1 |
| 2 | 7 | 2 |
| 3 | 8 | 1 |
| 3 | 9 | 3 |
+------+---------+----+
如果我想知道每个对手和Pc1交过多少次,结果应该是:
{2 => 2, 3 => 1}
因为 Pc2 和 Pc1 打过两场比赛,而 Pc3 只和 Pc1 打过一次。
我不在乎Pc1本身是否出现在结果中,我只是在之后不处理它。
对 Pc2 的相同查询应该 return:
{1 => 2, 3 => 0}
因为Pc1和Pc1玩过两次,而Pc3没有和Pc2玩过任何游戏。
我已经尝试了一段时间,但我似乎做不好。
提前致谢!
编辑 1
我想我找到了一个原始的 sql 查询来完成这项工作(仍然需要测试它)。看了一下,好像不能用ActiveRecord的查询方式来完成。查询如下:
sql = "
SELECT
pc_id,
COUNT(DISTINCT(game_id))
FROM
(SELECT
g.id as game_id,
p.pc_id as pc_id
FROM games g
INNER JOIN game_projects gp ON gp.game_id = g.id
INNER JOIN projects p ON p.id = gp.project_id
WHERE g.id IN (SELECT g2.id FROM games g2 JOIN game_projects gp2 ON gp2.game_id = g2.id JOIN projects p2 ON p2.id = gp2.project_id AND p2.pc_id = #{pc.id})
) AS t
GROUP BY pc_id;"
ActiveRecord::Base.connection.execute(sql).to_a
编辑 2
我似乎找到了将查询和子查询一分为二的解决方案:
game_ids = Game.joins(:projects).where('projects.pc_id = ?', pc.id).map(&:id)
Pc.joins(:projects => :games).where('games.id IN (?)', game_ids).group('pcs.id').count
甚至更简单,第二个是:
Project.joins(:games).where('games.id IN (?)', game_ids).group('projects.pc_id').count
编辑 3
I don't care if Pc1 itself appears in the result, I'll just not process it afterwards.
为避免这种情况,我们可以将 AND projects.pc_id != ?
添加到 WHERE
条件中。
我最终将查询一分为二,这样就简单多了。
game_ids = Game.joins(:projects).where('projects.pc_id = ?', pc.id).map(&:id)
Project.joins(:games).where('games.id IN (?) AND projects.pc_id != ?', game_ids, pc.id).group('projects.pc_id').count
我正在使用 Rails 3.2。我的模式相当复杂但很简单,我有个人电脑(代表玩家)、游戏和项目,因此:
class Game < ActiveRecord::Base
has_many :projects
class Pc < ActiveRecord::Base
has_many :projects
class Project < ActiveRecord::Base
has_many :games
belongs_to :pc
注意:GameProject 还有一个 table 以及多对多关联。
我试图找到一个查询(ActiveRecord 优于完全原始的 SQL)这样,给定一台 Pc,returns 是其他每台 Pc 玩过的游戏数量反对他们。如果同一游戏中有两个 Pc 的项目,则一个 Pc 与另一个 Pc 对战。示例情况是:
+------+---------+----+
| Game | Project | Pc |
+------+---------+----+
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 1 | 4 | 2 |
| 1 | 5 | 2 |
| 2 | 6 | 1 |
| 2 | 7 | 2 |
| 3 | 8 | 1 |
| 3 | 9 | 3 |
+------+---------+----+
如果我想知道每个对手和Pc1交过多少次,结果应该是:
{2 => 2, 3 => 1}
因为 Pc2 和 Pc1 打过两场比赛,而 Pc3 只和 Pc1 打过一次。 我不在乎Pc1本身是否出现在结果中,我只是在之后不处理它。
对 Pc2 的相同查询应该 return:
{1 => 2, 3 => 0}
因为Pc1和Pc1玩过两次,而Pc3没有和Pc2玩过任何游戏。
我已经尝试了一段时间,但我似乎做不好。
提前致谢!
编辑 1
我想我找到了一个原始的 sql 查询来完成这项工作(仍然需要测试它)。看了一下,好像不能用ActiveRecord的查询方式来完成。查询如下:
sql = "
SELECT
pc_id,
COUNT(DISTINCT(game_id))
FROM
(SELECT
g.id as game_id,
p.pc_id as pc_id
FROM games g
INNER JOIN game_projects gp ON gp.game_id = g.id
INNER JOIN projects p ON p.id = gp.project_id
WHERE g.id IN (SELECT g2.id FROM games g2 JOIN game_projects gp2 ON gp2.game_id = g2.id JOIN projects p2 ON p2.id = gp2.project_id AND p2.pc_id = #{pc.id})
) AS t
GROUP BY pc_id;"
ActiveRecord::Base.connection.execute(sql).to_a
编辑 2
我似乎找到了将查询和子查询一分为二的解决方案:
game_ids = Game.joins(:projects).where('projects.pc_id = ?', pc.id).map(&:id)
Pc.joins(:projects => :games).where('games.id IN (?)', game_ids).group('pcs.id').count
甚至更简单,第二个是:
Project.joins(:games).where('games.id IN (?)', game_ids).group('projects.pc_id').count
编辑 3
I don't care if Pc1 itself appears in the result, I'll just not process it afterwards.
为避免这种情况,我们可以将 AND projects.pc_id != ?
添加到 WHERE
条件中。
我最终将查询一分为二,这样就简单多了。
game_ids = Game.joins(:projects).where('projects.pc_id = ?', pc.id).map(&:id)
Project.joins(:games).where('games.id IN (?) AND projects.pc_id != ?', game_ids, pc.id).group('projects.pc_id').count