Rails - 不在特定关联旁边的联接 table 中的记录的范围
Rails - scope for records that are not in a join table alongside a specific association
我在 Rails 应用程序中有两个模型 - Tournament
和 Player
通过联接关联 table:
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}) }
成功找到所有已添加到锦标赛中的玩家,将锦标赛作为参数。
我想要的是一个相反的范围 - returns 所有尚未添加到给定锦标赛的玩家。我试过了
scope :not_selected, -> (tournament) { includes(:tournaments).where.not(tournaments: {id: tournament.id}) }
但是 returns 许多相同的玩家,我认为是因为这些玩家作为其他锦标赛的一部分存在。 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" != )
ORDER BY "players"."name" ASC [["id", 22]]
我也试过 this question 上的建议 - 使用
scope :not_selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: nil}) }
但这似乎不起作用 - 它只是 returns 一个空数组,我再次认为是因为玩家存在于加入 table 中作为单独锦标赛的一部分。 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
好的,试试这个:
includes(:tournaments).distinct.where.not(tournaments: {id: tournament.id})
您需要做的是:
- 使用参考 table 进行左连接,并在比赛 ID 上附加一个条件,该 ID 与您要为
查找未选中玩家的 ID 相匹配
- 应用 WHERE 子句表明没有进行 JOIN。
这段代码应该可以做到:
# 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 查询就好了...
一些注意事项:
- 不要加入实际关系(即锦标赛),它会显着降低查询性能,而且没有必要,因为所有条件先决条件都在参考 table 中。此外,您感兴趣的所有行 return 来自锦标赛 table.
的 NULL 数据
- 不要使用
eager_load
。除了据我所知它不支持自定义条件,它会为所有你不需要的相关对象创建模型。
我在 Rails 应用程序中有两个模型 - Tournament
和 Player
通过联接关联 table:
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}) }
成功找到所有已添加到锦标赛中的玩家,将锦标赛作为参数。
我想要的是一个相反的范围 - returns 所有尚未添加到给定锦标赛的玩家。我试过了
scope :not_selected, -> (tournament) { includes(:tournaments).where.not(tournaments: {id: tournament.id}) }
但是 returns 许多相同的玩家,我认为是因为这些玩家作为其他锦标赛的一部分存在。 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" != )
ORDER BY "players"."name" ASC [["id", 22]]
我也试过 this question 上的建议 - 使用
scope :not_selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: nil}) }
但这似乎不起作用 - 它只是 returns 一个空数组,我再次认为是因为玩家存在于加入 table 中作为单独锦标赛的一部分。 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
好的,试试这个:
includes(:tournaments).distinct.where.not(tournaments: {id: tournament.id})
您需要做的是:
- 使用参考 table 进行左连接,并在比赛 ID 上附加一个条件,该 ID 与您要为 查找未选中玩家的 ID 相匹配
- 应用 WHERE 子句表明没有进行 JOIN。
这段代码应该可以做到:
# 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 查询就好了...
一些注意事项:
- 不要加入实际关系(即锦标赛),它会显着降低查询性能,而且没有必要,因为所有条件先决条件都在参考 table 中。此外,您感兴趣的所有行 return 来自锦标赛 table. 的 NULL 数据
- 不要使用
eager_load
。除了据我所知它不支持自定义条件,它会为所有你不需要的相关对象创建模型。