繁体   English   中英

涉及多个表的复杂SQL查询

[英]Complicated SQL Query involving multiple tables

在这个查询中,我必须列出一对玩家ID和玩家名称的球员,他们为同一支球队效力。如果一名球员为3支球队效力,则另一支球员必须参加完全相同的3支球队。 不能少,不多了。 如果两名球员目前不参加任何球队,他们也应该被包括在内。 查询应该返回(playerID1,playername1,playerID2,playerName2)而没有重复,例如如果玩家1信息在玩家2之前出现,则不应该有另一个玩家2信息在玩家1之前出现的元组。

例如,如果玩家A为洋基队和红袜队队员比赛,而队员队员为洋基队队员,红袜队队员和道奇队队员队效力,我就不应该参加比赛。 他们都必须为洋基队和红袜队效力,而不是其他人。 现在,如果玩家为同一个团队玩游戏,此查询会找到答案。

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

现在我的查询是

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

在此之后,我被困在如何接近它。 有关如何解决此问题的任何提示。 谢谢你的时间。

我认为最简单的方法是将团队连接在一起,然后加入结果。 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;

编辑:

你可以在没有string_agg情况下做到这一点。 我们的想法是从所有可能的玩家组合列表开始。

然后,使用left outer join第一个玩家的团队。 并通过在团队和驱动程序名称上使用full outer join和匹配来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(*);

这会加入团队ID上的两个玩家(有序,所以一对只被考虑一次)。 然后,当两个玩家的所有行都具有非NULL团队值时,它会说匹配。 对于等效的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;

当两个玩家拥有不匹配的球队时,完整的外部联接将产生NULL值。 因此,没有NULL值意味着所有团队都匹配。

这是对你前一个问题答案的改编。

  1. 使用三角形连接获取所有玩家的独特组合:

     SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName FROM player p1 INNER JOIN player p2 ON p1.playerID < p2.playerID 
  2. 从第一个玩家中减去第二个玩家的团队集,并检查结果中是否没有行:

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

     NOT EXISTS ( SELECT teamID FROM plays WHERE playerID = p2.playerID EXCEPT SELECT teamID FROM plays WHERE playerID = p1.playerID ) 
  4. 最后,将两个条件应用于步骤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