【问题标题】:Complicated SQL Query involving multiple tables涉及多个表的复杂 SQL 查询
【发布时间】:2013-07-31 02:53:06
【问题描述】:

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

例如,如果球员 A 为洋基队和红袜队效力,球员 b 为洋基队、红袜队和道奇队效力,我不应该得到他们。他们都必须为洋基队和红袜队效力,而没有其他人。现在,如果球员为同一支球队效力,这个查询可以找到答案。

 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 并创建了一个小型数据库进行测试,但我不认为要解决这个问题,我们需要一个数据库。抱歉,术语不太好。刚开始学习 SQL。
  • PostgreSQL 的哪个版本?

标签: sql relational-database


【解决方案1】:

我认为最简单的方法是将团队连接在一起,然后加入结果。 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 并匹配团队和车手姓名加入第二个团队。您需要驱动程序表的原因是确保 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 值意味着所有团队都匹配。

【讨论】:

  • 有没有办法在不使用 string-agg 的情况下做到这一点。抱歉,我不熟悉这个概念。
  • 非常感谢,现在说得通了。唯一的问题。最终连接是什么意思?
  • 实际上它给了我为同一支球队效力的球员,而不是为完全相同球队效力的球员。例如,如果球员 A 为洋基队和红袜队效力,球员 b 为洋基队、红袜队和道奇队效力,我不应该得到他们。他们都必须为洋基队和红袜队效力,而没有其他人。现在,如果球员为同一支球队效力,这个查询可以找到答案。
  • 驱动是从哪里来的?
  • 驱动是所有玩家对的交叉连接。这用于后续的连接,解决full outer join导致player2id变为NULL的问题。
【解决方案2】:

这是 my answer 对您的 previous question 的改编。

  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
        )
    ;
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多