具有多个表和关系的复杂SQL查询
在这个查询中,我必须列出一对球员及其playerID和playerName,他们为完全相同的球队比赛。如果一名球员为3支球队比赛,另一名球员必须为完全相同的3支球队比赛。不能少,不能多。如果有两名球员目前没有参加任何球队的比赛,他们也应该包括在内。查询应该返回(playerID1、playername1、playerID2、playerName2),不重复,例如如果player1信息在player2之前,那么不应该有另一个包含player2信息的元组在player1之前 例如,如果球员A为洋基队和红袜队效力,而球员b为洋基队、红袜队和道奇队效力,我就不应该得到他们。他们都必须为洋基队,红袜队而不是其他任何人效力。现在,如果球员为同一支球队踢球,这个查询就会找到答案具有多个表和关系的复杂SQL查询,sql,postgresql,Sql,Postgresql,在这个查询中,我必须列出一对球员及其playerID和playerName,他们为完全相同的球队比赛。如果一名球员为3支球队比赛,另一名球员必须为完全相同的3支球队比赛。不能少,不能多。如果有两名球员目前没有参加任何球队的比赛,他们也应该包括在内。查询应该返回(playerID1、playername1、playerID2、playerName2),不重复,例如如果player1信息在player2之前,那么不应该有另一个包含player2信息的元组在player1之前 例如,如果球员A为洋基队
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