Home > Software design >  SQL: how to get membership based on ALL from a given cohort
SQL: how to get membership based on ALL from a given cohort

Time:01-21

I have the following tables in a MySQL database:

team

team_id   name
3         Rangers
12        Capitals
19        Red Wings
4         Bruins
212       Avalanche
102       Flyers
20        Islanders
50        Sabres
7         Stars

player

id    name
2     Zach
1     Deb
17    William
9     Viktor
12    Andrew
41    Chris
22    Bobby
5     Phil
3     Roy
92    Li
6     Juan

players_in

team_id   player_id   points
3         2           42
212       2           19
3         12          18
19        12          2
3         41          2
4         41          1
212       41          78
212       17          1
19        41          4
12        41          2
3         17          6
4         1           9
102       1           40
102       22          7
20        22          19
20        5           22
50        3           20
12        92          15
12        17          8
7         6           12

Here is a SQL Fiddle with the data: http://www.sqlfiddle.com/#!9/989ebe/1

I would like to get the name and id of the players who have played on ALL of the teams that Zach has played on.

In this case, Zach has played for the Rangers and the Avalanche.

Therefore, the desired result set would be:

name       id
William    17
Chris      41

(because these players were part of both the Rangers and the Avalanche teams)

How would I do this?

Thanks!

CodePudding user response:

Using a cte for Zach's games and then checking all potential memberships based on team_id existence in the cte's values:

with cte as (
   select pi1.team_id from players_in pi1 join player p2 on p2.id = pi1.player_id 
   where p2.name = 'Zach'
)
select p.* from player p where (select count(*) from cte c) = (select 
    sum(pi1.team_id in (select c.team_id from cte c)) 
    from players_in pi1 where pi1.player_id = p.id) and p.name != 'Zach'

See fiddle.

CodePudding user response:

select distinct p.* 
from player p
  join players_in pi on pi.player_id = p.id
  join player p2 on p2.name = 'Zach'
  join players_in pi2 on pi2.team_id = pi.team_id
                and pi2.player_id = p2.id
where
  p.name <> 'Zach'
  and not exists (select 1 from players_in pi3                   
                  where pi3.player_id = p2.id
                      and pi3.team_id not in (select team_id 
                                              from players_in pi4 
                                              where pi4.player_id = p.id));

First of all I've joined players_in (pi) with players (p) obtaining the set of all players and theirs teams.

Second, cross joined player zack joined with player_in (pi2) obtaining the set of Zach's teams. Joined pi2 with pi I've obtained the set of all player that had played in a Zach's team.

Now the where conditions:

  • p.name <> 'Zach' will exclude Zach from my list.
  • The not exists condition is the hard part of the query. I've selected all Zach teams again (pi3) not in the set of the player's (p) team,

SQL Fiddle here

CodePudding user response:

Your requirement could be translated to: searching for players which there's not exists any Jack's team that they don't play in. Corresponding query could be:

SELECT
  DISTINCT p1.name, p1.id
FROM
  player p1 
  INNER JOIN players_in pin1 ON p1.id = pin1.player_id
WHERE
  name != 'Zach'
  AND NOT EXISTS (
    SELECT 1
    FROM 
      team t 
      INNER JOIN players_in pin2 ON t.team_id = pin2.team_id
      INNER JOIN player p2 ON p2.id = pin2.player_id
    WHERE 
      p2.name = 'Zach' 
      AND NOT EXISTS (SELECT 1 
                      FROM players_in pin3 
                      WHERE pin2.team_id = pin3.team_id 
                        AND pin1.player_id = pin3.player_id)
  );

Demo: http://www.sqlfiddle.com/#!9/989ebe/61

CodePudding user response:

team_id name 3 Rangers 12 Capitals 19 Red Wings 4 Bruins 212 Avalanche 102 Flyers 20 Islanders 50 Sabres 7 Stars

  • Related