Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有多个表和关系的复杂SQL查询_Sql_Postgresql - Fatal编程技术网

具有多个表和关系的复杂SQL查询

具有多个表和关系的复杂SQL查询,sql,postgresql,Sql,Postgresql,在这个查询中,我必须列出一对球员及其playerID和playerName,他们为完全相同的球队比赛。如果一名球员为3支球队比赛,另一名球员必须为完全相同的3支球队比赛。不能少,不能多。如果有两名球员目前没有参加任何球队的比赛,他们也应该包括在内。查询应该返回(playerID1、playername1、playerID2、playerName2),不重复,例如如果player1信息在player2之前,那么不应该有另一个包含player2信息的元组在player1之前 例如,如果球员A为洋基队

在这个查询中,我必须列出一对球员及其playerID和playerName,他们为完全相同的球队比赛。如果一名球员为3支球队比赛,另一名球员必须为完全相同的3支球队比赛。不能少,不能多。如果有两名球员目前没有参加任何球队的比赛,他们也应该包括在内。查询应该返回(playerID1、playername1、playerID2、playerName2),不重复,例如如果player1信息在player2之前,那么不应该有另一个包含player2信息的元组在player1之前

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

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

Example data:
PLAYER    
playerID    playerName
1           Rondo
2           Allen
3           Pierce
4           Garnett
5           Perkins

TEAM      
teamID     teamName       sport
1          Celtics        Basketball
2          Lakers         Basketball
3          Patriots       Football
4          Red Sox        Baseball
5          Bulls          Basketball

PLAYS
playerID    TeamID
1           1
1           2
1           3
2           1
2           3
3           1
3           3
所以我应该得到这个答案-

 2, Allen, 3, Pierce 
 4, Garnett, 5, Perkins

2,艾伦,3,皮尔斯是一个弱者,因为他们都只为凯尔特人和爱国者效力 4岁的加内特,5岁的帕金斯是个答案,因为两名球员都不代表本应被淘汰的球队

现在我的问题是

SELECT p1.PLAYERID, 
       f1.PLAYERNAME, 
       p2.PLAYERID, 
       f2.PLAYERNAME 
FROM   PLAYER f1, 
       PLAYER f2, 
       PLAYS p1 
       FULL OUTER JOIN PLAYS p2 
                    ON p1.PLAYERID < p2.PLAYERID 
                       AND p1.TEAMID = p2.TEAMID 
GROUP  BY p1.PLAYERID, 
          f1.PLAYERID, 
          p2.PLAYERID, 
          f2.PLAYERID 
HAVING Count(p1.PLAYERID) = Count(*) 
       AND Count(p2.PLAYERID) = Count(*) 
       AND p1.PLAYERID = f1.PLAYERID 
       AND p2.PLAYERID = f2.PLAYERID; 
选择p1.PLAYERID,
f1.PLAYERNAME,
p2.PLAYERID,
f2.PLAYERNAME
来自f1选手,
玩家f2,
播放p1
完全外部联接
在p1.PLAYERID上
我不是100%确定,但我认为这会找到为同一支球队效力的球员,但我想找到为完全相同的球队效力的球员,如上所述


我被困在如何处理这件事之后。关于如何解决这个问题的任何提示。谢谢你抽出时间

我想到了两种可能的解决方案:

  • 光标-循环遍历每个玩家,并将其与所有其他玩家进行比较,直到得出结论
  • 递归查询-同样的想法,虽然稍微复杂一些,但显然是更好的方法。可能也有更好的表现

  • 您能否提供一些示例数据,以便我创建一个示例?

    我相信此查询将满足您的要求:

    SELECT array_agg(players), player_teams
    FROM (
      SELECT DISTINCT t1.t1player AS players, t1.player_teams
      FROM (
        SELECT
          p.playerid AS t1id,
          concat(p.playerid,':', p.playername, ' ') AS t1player,
          array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
        FROM player p
        LEFT JOIN plays pl ON p.playerid = pl.playerid
        GROUP BY p.playerid, p.playername
      ) t1
    INNER JOIN (
      SELECT
        p.playerid AS t2id,
        array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
      FROM player p
      LEFT JOIN plays pl ON p.playerid = pl.playerid
      GROUP BY p.playerid, p.playername
    ) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
    ) innerQuery
    GROUP BY player_teams
    
    它在
    游戏
    中的每个玩家的团队ID上使用数组_agg来匹配具有完全相同团队配置的玩家。例如,我在团队中包含了一个列,但只要不从GROUPBY子句中删除,就可以在不影响结果的情况下删除该列

    使用Postgesql 9.2.4进行测试


    编辑:修复了一个复制行的错误。

    您需要的基本数据类型似乎是集合,而不是数组。因此,一种选择可能是将PL/Python与下面类似的代码一起使用(请参阅本答案的底部,了解可能适合此目的的函数)。当然,这绝不是一种“纯SQL”方法

    但是坚持使用PostgreSQL(尽管不是标准SQL),您可能还希望使用DISTINCT with array_agg。请注意,以下仅给出满足标准的第一对(原则上可能还有更多)

    上面的查询提供以下输出:

     teams | playerid | playername | playerid | playername 
    -------+----------+------------+----------+------------
     {1,3} |        2 | Allen      |        3 | Pierce
           |        4 | Garnett    |        5 | Perkins
    (2 rows)
    
    PL/Python函数示例:

    CREATE OR REPLACE FUNCTION set(the_list integer[])
      RETURNS integer[] AS
    $BODY$
        return list(set(the_list))
    $BODY$
      LANGUAGE plpython2u;
    
    CREATE OR REPLACE FUNCTION pairs(a_set integer[])
      RETURNS SETOF integer[] AS
    $BODY$
        def pairs(x):
            for i in range(len(x)):
                for j in x[i+1:]:
                    yield [x[i], j]
        return list(pairs(a_set))
    $BODY$
      LANGUAGE plpython2u;
    
    SELECT set(ARRAY[1, 1, 2, 3, 4, 5, 6, 6]);
    
    上述使用这些函数的代码版本(输出类似,但当给定团队有多个对时,此方法选择所有对):


    看来OP可能不再感兴趣了但万一有人发现它有用, 这是在纯SQL中运行的查询(至少对我来说;)

    从播放器pr1中选择M.p1、pr1.playername、M.p2、pr2.playername
    内连接玩家pr2内连接
    (
    从plays plays1中选择plays1.player p1、plays2.player p2、plays1.team t1
    内部连接播放播放2
    打开(plays1.player
    我们对每位球员的球队数量以及ascii(球队名称)+球队id的总和进行查询,称之为球队值。我们对同一个查询本身进行自连接,其中counts和team_值匹配,但id不等于id,这给了我们想要获取的id

    select * from player where player_id in 
    (
     select set2.player_id orig
     from
     (select count(*) count,b.player_id , nvl(sum(a.team_id+ascii(team_name)),0) team_value
       from plays a, player b , team c
       where a.player_id(+)=b.player_id
        and a.team_id = c.team_id(+)
       group by b.player_id) set1,
    (select count(*) count,b.player_id , nvl(sum(a.team_id+ascii(team_name)),0) team_value
       from plays a, player b , team c
       where a.player_id(+)=b.player_id
        and a.team_id = c.team_id(+)
       group by b.player_id) set2
    where set1.count=set2.count and set1.team_value=set2.team_value
      and set1.player_id<>set2.player_id
    )
    
    从播放器id所在的播放器中选择*
    (
    选择set2.player\u id orig
    从…起
    (选择count(*)count,b.player\u id,nvl(总和(a.team\u id+ascii(team\u name)),0)team\u值
    来自a队,b队,c队
    其中a.player\u id(+)=b.player\u id
    和a.team_id=c.team_id(+)
    按b.player_id)集合1分组,
    (选择count(*)count,b.player\u id,nvl(总和(a.team\u id+ascii(team\u name)),0)team\u值
    来自a队,b队,c队
    其中a.player\u id(+)=b.player\u id
    和a.team_id=c.team_id(+)
    按b.player_id)集合2分组
    其中set1.count=set2.count和set1.team_value=set2.team_value
    和set1.player_idset2.player_id
    )
    
    没什么大不了的,这里有一个解决方案

    with gigo as(select a.playerid as playerid,count(b.teamname) as nteams from player a 
    full outer join plays c on a.playerid=c.playerid full outer join team b 
    on b.teamid=c.teamid group by a.playerid)
    select array_agg(a.*),g.nteams from player a inner join gigo g on a.playerid=g.playerid 
    group by g.nteams having count(a.*)>1 order by g.nteams desc
    

    下面是带有UNION和2-3个简单联接的简单查询。 工会前的第一个查询包含球员姓名和球员ID,他们在相同数量的球队中踢了相同的次数。 工会后的第二个查询包含球员姓名和球员ID,他们根本没有为任何球队效力过

    只需复制粘贴此查询并尝试执行它,您将看到预期的结果

        select playername,c.playerid from 
        (select a.cnt, a.playerid from 
        (select count(1) cnt , PLAYERID from plays group by  PLAYERID) a ,
        (select count(1) cnt , PLAYERID from plays group by  PLAYERID) b 
        where a.cnt=b.cnt
        and  a.playerid<> b.playerid ) c ,PLAYER  d
        where c.playerid=d.playerid
        UNION
        select e.playername,e.playerid 
        from player e 
        left outer join plays f on 
        e.playerid=f.playerid where nvl(teamid,0 )=0
    
    从中选择playername、c.playerid
    (选择a.cnt、a.playeri)
    
    SELECT M.p1, pr1.playername, M.p2, pr2.playername FROM player pr1 
    INNER JOIN player pr2 INNER JOIN
    (
       SELECT plays1.player p1, plays2.player p2, plays1.team t1 FROM plays plays1 
       INNER JOIN plays plays2 
       ON (plays1.player < plays2.player AND plays1.team = plays2.team)
       GROUP BY plays1.player, plays2.player HAVING COUNT(*) = 
    ((SELECT COUNT(*) FROM plays plays3 WHERE plays3.player = plays1.player) + 
    (SELECT COUNT(*) FROM plays plays4 WHERE plays4.player = plays2.player)) /2
    ) M ON pr1.playerID = M.p1 AND pr2.playerID = M.p2 
    UNION ALL
    SELECT M.pid, M.pname, N.pid2, N.pname2 FROM
    (
    (SELECT p.playerID pid, p.playerName pname, pl.team FROM player p
     LEFT JOIN plays pl ON p.playerId = pl.player WHERE pl.team IS NULL) M
     INNER JOIN
     (SELECT p.playerID pid2, p.playerName pname2, pl.team FROM player p
      LEFT JOIN plays pl ON p.playerId = pl.player WHERE pl.team IS NULL) N 
     ON (pid < pid2)
    )
    
    select * from player where player_id in 
    (
     select set2.player_id orig
     from
     (select count(*) count,b.player_id , nvl(sum(a.team_id+ascii(team_name)),0) team_value
       from plays a, player b , team c
       where a.player_id(+)=b.player_id
        and a.team_id = c.team_id(+)
       group by b.player_id) set1,
    (select count(*) count,b.player_id , nvl(sum(a.team_id+ascii(team_name)),0) team_value
       from plays a, player b , team c
       where a.player_id(+)=b.player_id
        and a.team_id = c.team_id(+)
       group by b.player_id) set2
    where set1.count=set2.count and set1.team_value=set2.team_value
      and set1.player_id<>set2.player_id
    )
    
    with gigo as(select a.playerid as playerid,count(b.teamname) as nteams from player a 
    full outer join plays c on a.playerid=c.playerid full outer join team b 
    on b.teamid=c.teamid group by a.playerid)
    select array_agg(a.*),g.nteams from player a inner join gigo g on a.playerid=g.playerid 
    group by g.nteams having count(a.*)>1 order by g.nteams desc
    
        select playername,c.playerid from 
        (select a.cnt, a.playerid from 
        (select count(1) cnt , PLAYERID from plays group by  PLAYERID) a ,
        (select count(1) cnt , PLAYERID from plays group by  PLAYERID) b 
        where a.cnt=b.cnt
        and  a.playerid<> b.playerid ) c ,PLAYER  d
        where c.playerid=d.playerid
        UNION
        select e.playername,e.playerid 
        from player e 
        left outer join plays f on 
        e.playerid=f.playerid where nvl(teamid,0 )=0
    
    select group_concat(b.name),a.teams from
    (SELECT playerid, group_concat(distinct teamid ORDER BY teamid) AS teams
      FROM test
      GROUP BY playerid) a, player b
    where a.playerid=b.playerid
    group by a.teams
    union
    select group_concat(c.name order by c.playerid),null from player c where c.playerid not in (select        playerid from test);
    
    SELECT UNIQUE PLR1.PID,PLR1.PNAME, PLR2.PID, PLR2.PNAME
    FROM PLAYS PLY1,PLAYS PLY2, PLAYER PLR1, PLAYER PLR2
    WHERE PLR1.PID < PLR2.PID AND PLR1.PID = PLY1.PID(+) AND PLR2.PID = PLY2.PID(+)
    AND NOT EXISTS(( SELECT PLY3.TEAMID FROM PLAYS PLY3 WHERE PLY3.PID = PLR1.PID) 
    MINUS
    ( SELECT PLY4.TEAMID FROM PLAYS PLY4 WHERE PLY4.PID = PLR2.PID));
    
    WITH temp AS (
      SELECT p.playerid, p.playername, listagg(t.teamname,',') WITHIN GROUP (ORDER BY t.teamname) AS teams
      FROM player p full OUTER JOIN plays p1 ON p.playerid = p1.playerid
        LEFT JOIN team t ON p1.teamid = t.teamid GROUP BY (p.playerid , p.playername))
    SELECT concat(concat(t1.playerid,','), t1.playername), t1.teams 
    FROM temp t1 WHERE nvl(t1.teams,' ') IN (
      SELECT nvl(t2.teams,' ') FROM temp t2 
      WHERE t1.playerid <> t2.playerid) 
    ORDER BY t1.playerid
    
    SELECT TMP1. PLAYERID,TMP2.PLAYERID FROM
    (
        SELECT a.playerid , a.teamid,b.team_sum 
        FROM plays  A
        INNER JOIN 
        (
            SELECT PLAYERID,SUM(teamid) AS team_sum
            FROM plays
            GROUP BY 1
         ) B
    
        ON a.playerid=b.playerid
     ) TMP1
    
    INNER JOIN
    
    (
        SELECT a.playerid , a.teamid,b.team_sum
        FROM plays  A
    
        INNER JOIN 
        (
            SELECT PLAYERID,SUM(teamid) AS team_sum
            FROM plays
            GROUP BY 1
        ) B
    
    ON a.playerid=b.playerid
    
    )TMP2
    ON TMP1.PLAYERID < TMP2.PLAYERID
    AND TMP1.TEAMID=TMP2.TEAMID
    AND TMP1.TEAM_SUM=TMP2.TEAM_SUM
    GROUP BY 1,2
    
    UNION ALL
    SELECT n1,n2 FROM  
    (
        SELECT TMP3.PLAYERID AS n1,TMP4.PLAYERID AS n2 FROM 
        PLAYER  TMP3
        INNER JOIN PLAYER TMP4
        ON TMP3.PLAYERID<TMP4.PLAYERID
        WHERE TMP3.PLAYERID  NOT IN (SELECT  PLAYERID FROM plays  )
        AND tmp4.playerid NOT IN (SELECT playerid FROM plays)
    ) TMP5
    
    SELECT   TAB1.T1_playerID AS playerID1 , TAB1.playerName1  ,   
      TAB1.T2_playerID AS playerID2, TAB1. playerName2
     FROM
    (select   T1.playerID AS T1_playerID ,  T3. playerName  AS  playerName1 ,
    
    T2.playerID  AS T2_playerID ,  T4. playerName AS playerName2  ,COUNT (T1.TeamID) AS MATCHING_TEAM_ID_CNT
    FROM PLAYS T1
    INNER JOIN PLAYS T2  ON(  T1.TeamID = T2.TeamID AND T1.playerID <> T2.playerID )
    INNER JOIN player T3 ON (  T1.playerID=T3.playerID)
    INNER JOIN player T4 ON (  T2.playerID=T4.playerID)
     GROUP BY 1,2,3,4
    ) TAB1
    
    INNER JOIN 
    ( SELECT  T1.playerID AS playerID, COUNT(T1.TeamID) AS TOTAL_TEAM_CNT
     FROM PLAYS  T1
    GROUP BY T1.playerID) TAB2
    ON(TAB1.T2_playerID=TAB2.playerID AND    
      TAB1.MATCHING_TEAM_ID_CNT =TAB2.TOTAL_TEAM_CNT)
    
    INNER JOIN 
    ( SELECT  T1.playerID AS playerID, COUNT(T1.TeamID) AS TOTAL_TEAM_CNT
    FROM PLAYS  T1
    GROUP BY T1.playerID 
    ) TAB3
    ON( TAB1. T1_playerID = TAB3.playerID  AND 
     TAB1.MATCHING_TEAM_ID_CNT=TAB3.TOTAL_TEAM_CNT)
    WHERE playerID1  < playerID2
    
        UNION ALL (
        SELECT   T1.playerID, T1.playerName ,T2.playerID,T2.playerName
        FROM
        PLAYER T1 INNER JOIN PLAYER T2
        ON (T1.playerID<T2.playerID) 
        WHERE T1.playerID NOT IN ( SELECT playerID FROM PLAYS))
    
    ALTER FUNCTION [dbo].[fngetTeamIDs] ( @PayerID int ) RETURNS varchar(101) AS Begin
    
    declare @str varchar(1000)
    
    SELECT @str= coalesce(@str + ', ', '') + CAST(a.TeamID AS varchar(100)) FROM (SELECT DISTINCT TeamID from Plays where PayerId=@PayerID) a
    
    return @str
    
    END
    
    drop table #temp,#A,#B,#C,#D
    
    (select PayerID,count(*) count 
    into #temp
    from Plays 
    group by PayerID)
    
    
    select *
    into #A
    from #temp as T
    
    where T.count in (
            select T1.count from #temp as T1
            group by T1.count having count(T1.count)>1 
    )
    
    select A.*,P.TeamID
    into #B
    from #A A inner join Plays P
    on A.PayerID=P.PayerID
    order by A.count
    
    
    select B.PayerId,B.count, 
    (
    select dbo.fngetTeamIDs(B.PayerId)
    ) as TeamIDs
    into #C
    from #B B
    group by B.PayerId,B.count
    
    
    select TeamIDs 
    into #D
    from #c as C
    group by C.TeamIDs
    having count(C.TeamIDs)>1
    
    select C.PayerId,P.PlayerName,D.TeamIDs
    from #D D inner join #C C
    on D.TeamIDs=C.TeamIDs
    inner join Player P
    on C.PayerID=P.PlayerID
    
    SELECT 
         b.playerID
        ,b.playerName
    FROM (
    --Join the totals of teams to your player information and then count over the team matches.
            SELECT 
                    p.playerID
                    ,p.playerName
                    ,m.TeamMatches
                    ,COUNT(*) OVER(PARTITION BY TeamMatches) as Matches
            FROM player p
                    LEFT JOIN (
                    --Assuming your teamID is unique as it should be. If it is then a sum of the team ids for a player will give you each team they play for. 
                    --If for some reason your team id is not unique then rank the table and join same as below. 
                        SELECT 
                             ps.playerName
                            ,ps.playerID
                            ,SUM(t.teamID) as TeamMatches
                        FROM plays p
                                LEFT JOIN team t ON p.teamID = p.teamID
                                LEFT JOIN player ps ON p.playerID = ps.playerID
                        GROUP BY 
                                ps.playerName
                            ,ps.playerID
                    ) m ON p.playerID = m.playerID
    ) b
    WHERE
    b.Matches <> 1