【问题标题】:Reverse query on a has_many :through反向查询 has_many :through
【发布时间】:2017-08-16 01:03:58
【问题描述】:

场景:

Team
has_many :players, dependent: :destroy
has_many :users, through: :players

Player
belongs_to :team
belongs_to :user

User

所以,假设我有 4 个不同用户的团队:

Team 1
User 1, User 2

Team 2
User 2, User 3

Team 3
User 1

Team 4
User 2, User 4, User 5

现在,假设我有两个用户的 ID(User 1User 5),我想知道是否有任何团队由这两个玩家组成。假设我有一个由用户 1、2 和 5 组成的团队。查询不应该带来这个团队。

我如何使用ActiveRecord 语义来帮助我做到这一点?从一个团队中获得所有球员很容易,但我找不到相反的方法。

【问题讨论】:

  • 他们是否必须在同一个团队中,可能与其他用户一起,还是必须是该团队中唯一的玩家?
  • @MrYoshiji 那支球队中唯一的球员。

标签: sql ruby-on-rails rails-activerecord


【解决方案1】:

更新:啊!我用纯 SQL 得到它:

users = User.first(2)
Team.joins(:users).group('teams.id').having('SUM( CASE WHEN users.id in (?) THEN 1 ELSE -1 END ) = ?', users, users.count)

试试吧,让我知道它是否适合你(在这里工作:http://sqlfiddle.com/#!17/bb2a9/8 和同样的例子,但有 3 名玩家:http://sqlfiddle.com/#!17/bb2a9/10


这在 DB 级别上没有优化,因为它使用了很多 ruby​​/rails 代码,但是你可以试试吗?

users = User.first(2)
# find teams with that exact number of players associated
teams = Team.joins(:users).group('teams.id').having('COUNT(users.id) = ?', users.count)
# find players referencing to those teams with other users than the ones specified
players_to_ignore = Player.where(team_id: teams).where('user_id NOT IN (?)', users)
# get Teams where associated players id is not in the previous list
Team.where(id: teams).joins(:players).where('players.id NOT IN (?)', players_to_ignore)

【讨论】:

  • 我会尽力给你反馈
【解决方案2】:

您可以使用两个 where 子句:

一个用于获取所有Teams 恰好有两个用户。

Team.joins(:users).group("teams.id").having("count('distinct users.id') = 2").select("teams.id")

第二名是所有Teams 与用户15

Team.joins(:users).where('users.id in (?)', [1,5]).group("teams.id").having("count('distinct users.id') = 2").select("teams.id")

这两者的交集应该可以满足您的需求。 所以把它们结合起来:

Team.where(id: Team.joins(:users).group("teams.id").having("count('distinct users.id') = 2").select("teams.id")).where(id: Team.joins(:users).where('users.id in (?)', [1,5]).group("teams.id").having("count('distinct users.id') = 2").select("teams.id"))

【讨论】:

  • 上下文:我们正在寻找由用户 1 和 2 组成的团队。团队 #3 有用户 1 和用户 3。这个团队 #3 将由您的第一条指令找到(有 2 名玩家),并且然后再次找到,因为其中一个玩家的 id 在 [1,2] 中。
【解决方案3】:
Team.join(:users).where('users.id in (?)', [1,5]).
select { |team| team.users.map(&:id).sort == [1,5] }

上一个答案(对于预先编辑的问题)

这对你有用吗?

Team.join(:users).where('users.id in (?)', [1,5])

你可以在用户模型上做同样的事情

# user.rb
has_many :teams, through: :works
has_many :works, foreign_key: :user_id

回复您的编辑和评论

哈基:

Team.join(:users).where('users.id in (?)', [1,5]).
select { |team| team.users.map(&:id).sort == [1,5] }

更好?

SQL Select only rows where exact multiple relationships exist

【讨论】:

  • 这行不通,因为我需要只有这两名球员的球队。假设我有一个包含用户 1、2 和 5 的团队。这个查询也会带来这个团队。
  • @MurifoX 更新了答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-27
  • 1970-01-01
  • 2019-03-04
  • 2011-07-06
  • 1970-01-01
  • 2016-01-20
相关资源
最近更新 更多