【发布时间】:2020-05-22 08:20:20
【问题描述】:
我正在使用 Rails 3.2。我的架构相当复杂但很简单,我有 Pcs(代表玩家)、游戏和项目,例如:
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 以及多对多关联都有一个表。
我正在尝试查找一个查询(ActiveRecord 优于完全原始 SQL),这样,给定一台 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 的相同查询应该返回:
{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
我不在乎 Pc1 本身是否出现在结果中,我只是以后不处理它。
为避免这种情况,我们可以将AND projects.pc_id != ? 添加到WHERE 条件中。
【问题讨论】:
标签: mysql ruby-on-rails ruby-on-rails-3