Rails - 与特定关联一起不在连接表中的记录的范围

时间:2016-09-10 12:16:22

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

我在Rails应用中有两个模型 - TournamentPlayer通过连接表关联:

class Tournament < ApplicationRecord

  has_many :tournament_players
  has_many :players, through: :tournament_players

end


class Player < ApplicationRecord

  has_many :tournament_players
  has_many :tournaments, through: :tournament_players

  scope :selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: tournament.id}) }

end

我有很多锦标赛,每个锦标赛都有很多玩家。玩家可以参加很多锦标赛。范围

scope :selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: tournament.id}) }

成功找到已经加入锦标赛的所有球员,因为锦标赛是一个参数。

我喜欢的是一个相反的范围 - 返回尚未添加到特定锦标赛的所有玩家。我试过了

scope :not_selected, -> (tournament) { includes(:tournaments).where.not(tournaments: {id: tournament.id}) }

但这会让许多相同的球员回归,我认为因为球员是其他锦标赛的一部分。 SQL的内容类似于:

SELECT "players".*, "tournaments”.* FROM "players" LEFT OUTER JOIN
"tournament_players" ON "tournament_players"."player_id" =
"players"."id" LEFT OUTER JOIN "tournaments" ON "tournaments"."id" =
"tournament_players"."tournament_id" WHERE ("tournaments"."id" != $1)
ORDER BY "players"."name" ASC  [["id", 22]]

我还尝试了this question上的建议 - 使用

scope :not_selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: nil}) }

但这似乎不起作用 - 它只返回一个空数组,我认为,因为玩家作为单独比赛的一部分存在于联接表中。 SQL的内容类似于:

SELECT "players”.*, "tournaments”.* FROM "players" LEFT OUTER JOIN
"tournament_players" ON "tournament_players"."player_id" = 
"players"."id" LEFT OUTER JOIN "tournaments" ON "tournaments"."id" = 
"tournament_players"."tournament_id" WHERE "tournaments"."id" IS NULL 
ORDER BY "players"."name" ASC

2 个答案:

答案 0 :(得分:4)

您需要做的是:

  1. 与参考表进行左联接,锦标赛ID上的附加条件与您想要找到未选择参与者的锦标赛ID相匹配
  2. 应用WHERE子句,表示没有JOIN。
  3. 此代码应该这样做:

    # player.rb
    scope :not_selected, -> (tournament) do 
      joins("LEFT JOIN tournament_players tp ON players.id = tp.player_id AND tp.tournament_id = #{tournament.id}").where(tp: {tournament_id: nil})
    end
    

    如果只有Rails有更好的方法来编写具有附加条件的LEFT JOIN查询......

    一些注意事项:

    1. 不加入实际关系(即锦标赛),它会大大降低查询的性能,而且这是不必要的,因为所有条件先决条件都在参考表中。此外,您感兴趣的所有行都会从锦标赛表中返回NULL数据。
    2. 请勿使用eager_load。除了我最了解它不支持自定义条件外,它还会为所有相关对象创建模型,而这些模型是您不需要的。

答案 1 :(得分:0)

好吧试试这个:

includes(:tournaments).distinct.where.not(tournaments: {id: tournament.id})