简体   繁体   English

涉及多个表的复杂SQL查询

[英]Complicated SQL Query involving multiple tables

In this Query, I have to list pair of players with their playerID and playerName who play for the exact same teams.If a player plays for 3 teams, the other has to play for exact same 3 teams. 在这个查询中,我必须列出一对玩家ID和玩家名称的球员,他们为同一支球队效力。如果一名球员为3支球队效力,则另一支球员必须参加完全相同的3支球队。 No less, no more. 不能少,不多了。 If two players currently do not play for any team, they should also be included. 如果两名球员目前不参加任何球队,他们也应该被包括在内。 The query should return (playerID1, playername1, playerID2, playerName2) with no repetition such as if player 1 info comes before player 2, there should not be another tuple with player 2 info coming before player 1. 查询应该返回(playerID1,playername1,playerID2,playerName2)而没有重复,例如如果玩家1信息在玩家2之前出现,则不应该有另一个玩家2信息在玩家1之前出现的元组。

For example if player A plays for yankees and redsox, and player b plays for Yankees, Red Sox, and Dodgers I should not get them. 例如,如果玩家A为洋基队和红袜队队员比赛,而队员队员为洋基队队员,红袜队队员和道奇队队员队效力,我就不应该参加比赛。 They both have to play for Yankees, and Red Sox and no one else. 他们都必须为洋基队和红袜队效力,而不是其他人。 Right now this query finds answer if players play for any same team. 现在,如果玩家为同一个团队玩游戏,此查询会找到答案。

 player(playerID: integer, playerName: string)
 team(teamID: integer, teamName: string, sport: string)
 plays(playerID: integer, teamID: integer)

Right now the Query I have is 现在我的查询是

SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1, player p2, plays
WHERE p1.teamID = p2.teamID AND teamID in.....

I am stuck on how to approach it after this. 在此之后,我被困在如何接近它。 Any hints on how to approach this problem. 有关如何解决此问题的任何提示。 Thanks for your time. 谢谢你的时间。

I think the easiest approach is to concatenate the teams together and just join on the results. 我认为最简单的方法是将团队连接在一起,然后加入结果。 Postgres provides the function string_agg() to aggregate strings: Postgres提供函数string_agg()来聚合字符串:

select p1.playerId, p1.playerName, p2.playerId, p2.playerName
from (select p.playerId, string_agg(cast(p.TeamId as varchar(255)), ',' order by TeamId) as teams,
             pp.PlayerName
      from plays p join
           players pp
           on p.playerId = pp.playerId
      group by p.playerId
     ) p1 join
     (select p.playerId, string_agg(cast(p.TeamId as varchar(255)), ',' order by TeamId) as teams,
             pp.PlayerName
      from plays p join
           players pp
           on p.playerId = pp.playerId
      group by p.playerId
     ) p2
     on p1.playerid < p2.playerid and p1.teams = p2.teams;

EDIT: 编辑:

You can do this without string_agg . 你可以在没有string_agg情况下做到这一点。 The idea is to start with a list of all possible player combinations. 我们的想法是从所有可能的玩家组合列表开始。

Then, join in the teams for the first player using left outer join . 然后,使用left outer join第一个玩家的团队。 And join in the teams for the second by using full outer join and matching on the team and driver name. 并通过在团队和驱动程序名称上使用full outer join和匹配来full outer join第二个团队。 The reason you need the driver table is to be sure that the id/name does not get lost in the full outer join: 您需要驱动程序表的原因是为了确保id / name不会在完全外连接中丢失:

select driver.playerid1, driver.playerid2
from (select p1.playerId as playerId1, p1.playerName as playerName1,
             p2.playerId as playerId2, p1.playerName as playerName2
      from players p1 cross join
           players p2
      where p1.playerId < p2.playerId
     ) driver left outer join
     plays p1
     on p1.playerId = driver.playerId full outer join
     plays p2
     on p2.playerId = driver.playerId and
        p2.teamid = p1.teamid
group by driver.playerid1, driver.playerid2
having count(p1.playerid) = count(*) and
       count(p2.playerid) = count(*);

This joins two players on the team id (with ordering so a pair only gets considered once). 这会加入团队ID上的两个玩家(有序,所以一对只被考虑一次)。 It then says there is a match when all the rows for the two players have non-NULL team values. 然后,当两个玩家的所有行都具有非NULL团队值时,它会说匹配。 This is perhaps more clear with the equivalent having clause: 对于等效的having子句,这可能更清楚:

having sum(case when p1.playerid is null then 1 else 0 end) = 0 and
       sum(case when p2.playerid is null then 1 else 0 end) = 0;

The full outer join will produce NULL values when two players have teams that don't match. 当两个玩家拥有不匹配的球队时,完整的外部联接将产生NULL值。 So, no NULL values mean that all the teams match. 因此,没有NULL值意味着所有团队都匹配。

This is an adaptation of my answer to a previous question of yours. 这是对你前一个问题答案的改编。

  1. Get all unique combinations of players using a triangular join: 使用三角形连接获取所有玩家的独特组合:

     SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName FROM player p1 INNER JOIN player p2 ON p1.playerID < p2.playerID 
  2. Subtract the second player's team set from that of the first player and check if there are no rows in the result: 从第一个玩家中减去第二个玩家的团队集,并检查结果中是否没有行:

     NOT EXISTS ( SELECT teamID FROM plays WHERE playerID = p1.playerID EXCEPT SELECT teamID FROM plays WHERE playerID = p2.playerID ) 
  3. Swap the sets, subtract and check again: 交换集合,减去并再次检查:

     NOT EXISTS ( SELECT teamID FROM plays WHERE playerID = p2.playerID EXCEPT SELECT teamID FROM plays WHERE playerID = p1.playerID ) 
  4. Finally, apply both conditions to the result of the triangular join in Step 1. 最后,将两个条件应用于步骤1中三角形连接的结果。

     SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName FROM player p1 INNER JOIN player p2 ON p1.playerID < p2.playerID WHERE NOT EXISTS ( SELECT teamID FROM plays WHERE playerID = p1.playerID EXCEPT SELECT teamID FROM plays WHERE playerID = p2.playerID ) AND NOT EXISTS ( SELECT teamID FROM plays WHERE playerID = p2.playerID EXCEPT SELECT teamID FROM plays WHERE playerID = p1.playerID ) ; 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM