开发者

Rails 3 / SQL - A complex "exists?" query

开发者 https://www.devze.com 2023-04-04 06:18 出处:网络
My models class Team < ActiveRecord::Base has_many :team_players has_many :players, :through => :team_players

My models

class Team < ActiveRecord::Base
  has_many :team_players
  has_many :players, :through => :team_players
end

class TeamPlayer < ActiveRecord::Base
  belongs_to :team
  belongs_to :player
end
  • Teams can have a different number of players
  • Players can be on many teams
  • Retrieving a team_id if it already exists <- My problem

When creating a new team, I'm basically choosing multiple players in a nested form. Upon create, I need to check if a team with the exact player composition already exist.

Example:

Lets say I create a new team with A and B, then I need the query somehow tell me if it exists or not, by returning the team_id or something, or an empty result.

I've played around with queries similar to this, but with no satisfying result

SELECT *, count(id) as c FROM "team_players" WHERE player_id IN (1,3) GROUP BY team_id HAVING c = 2


I think you can do this in two steps:

  1. Find the teams that have all the players you're looking at.
  2. Check if those teams have more players than the player set you're looking at.

That translates into this:

select team_id
from team_players
where team_id in (
    select team_id
    from team_players
    where player_id in (1,3)
    group by team_id
    having count(player_id) = 2
)
group by team_id
having count(player_id) = 2

Given this in team_players:

team_id|player_id
     1 | 1
     1 | 3
     1 | 4
     2 | 1
     2 | 3
     3 | 3

The above query says team_id = 2 and there's your exact match. The query you're using gives you the teams that contain the players in question as a subset, not the teams that are set-wise equal to the players in question.

If you only want to know if such a team exists then wrap that query in a select_rows call and you're done:

class Team < ActiveRecord::Base
    # players is an array of Fixnum player IDs
    def self.team_exist?(players)
        return false if(!players.present?)
        connection.select_rows(%Q{
            select team_id
            from team_players
            where team_id in (
                select team_id
                from team_players
                where player_id in (#{players.join(',')})
                group by team_id
                having count(player_id) = #{players.length}
            )
            group by team_id
            having count(player_id) = #{players.length}
        }).present?
    end
end

The #{players.join(',')} interpolation assumes that team_exist? is being given an array of Fixnum so you'll want to properly scrub your data in the caller or add a players = players.map(&:to_i) before the connect.select_rows call to scrub it inside team_exist?.

0

精彩评论

暂无评论...
验证码 换一张
取 消