排除非排他性的价值对

时间:2014-08-08 23:33:58

标签: sql oracle

这个问题难以用英语描述,所以我会给出一些例子。

我有一张名为Games的表。每个游戏由游戏编号和玩家组成,分为两列:GameNum,PlayerNum。

我的问题是,我想选择那些在游戏中互相玩过的玩家,以及没有其他人

以下是一些示例数据:

GameNum PlayerNum
1       100
1       101
2       102
2       103
3       102
3       104
4       105
4       106
5       106
5       107
6       100
6       101

我希望返回结果:

PlayerNum1 PlayerNum2
100        101

这是因为我们可以看到玩家100和101是唯一一个彼此玩游戏的玩家,而没有其他玩家。 102也玩了104,所以我们排除102和104.虽然105只玩了106的游戏,106也玩了107游戏,所以我们排除玩家105和106(因此107)的结果。这让我们只有100和101的球员。

每个GameNum只会在表格中出现两次(即每个游戏总是会有两个玩家)。另请注意,我们并不关心玩家是否一起玩过多个游戏(例如GameNum 1和6) - 前提是他们只是互相玩过这些游戏。

我尝试使用min / max进行类似下面的查询,但我无法弄清楚如何排除与其他玩家玩过游戏的玩家。

SELECT *
FROM
(
    SELECT AU1.PlayerNum AS PlayerNum1, AU2.PlayerNum AS PlayerNum2
    FROM
    (
      SELECT GameNum, MIN(PlayerNum) AS PlayerNum
      FROM GAMES
      GROUP BY GameNum
      HAVING count(GameNum) = 2
    ) AU1
    INNER JOIN 
    (
      SELECT GameNum, MAX(PlayerNum) AS PlayerNum
      FROM GAMES
      GROUP BY GameNum
      HAVING count(GameNum) = 2
    ) AU2
    ON AU2.GameNum = AU1.GameNum
) T2
GROUP BY T2.PlayerNum1, T2.PlayerNum2
ORDER BY T2.PlayerNum1, T2.PlayerNum2;

非常感谢! :)

编辑:以下是使用上述数据创建表格基本版本的查询:

CREATE TABLE Games
(
    GameNum int,
    PlayerNum int
);

INSERT INTO Games (GameNum, PlayerNum) VALUES (1, 100);
INSERT INTO Games (GameNum, PlayerNum) VALUES (1, 101);
INSERT INTO Games (GameNum, PlayerNum) VALUES (2, 102);
INSERT INTO Games (GameNum, PlayerNum) VALUES (2, 103);
INSERT INTO Games (GameNum, PlayerNum) VALUES (3, 102);
INSERT INTO Games (GameNum, PlayerNum) VALUES (3, 104);
INSERT INTO Games (GameNum, PlayerNum) VALUES (4, 105);
INSERT INTO Games (GameNum, PlayerNum) VALUES (4, 106);
INSERT INTO Games (GameNum, PlayerNum) VALUES (5, 106);
INSERT INTO Games (GameNum, PlayerNum) VALUES (5, 107);
INSERT INTO Games (GameNum, PlayerNum) VALUES (6, 100);
INSERT INTO Games (GameNum, PlayerNum) VALUES (6, 101);
INSERT INTO Games (GameNum, PlayerNum) VALUES (5, 107);
INSERT INTO Games (GameNum, PlayerNum) VALUES (6, 100);
INSERT INTO Games (GameNum, PlayerNum) VALUES (6, 101);

3 个答案:

答案 0 :(得分:2)

尝试类似这样的东西(mysql语法):

select distinct least(t1.p1, t2.p2), greatest(t1.p1, t2.p2)
from
(
 select p1, max(p2) as p2
 from (select min(PlayerNum) as p1, max(PlayerNum) as p2 from GAMES group by GameNum union select max(PlayerNum) as p1, min(PlayerNum) as p2 from GAMES group by GameNum) as q1
 group by q1.p1
 having count(distinct p2)=1
) as t1
,
(
 select min(p1) as p1, p2
 from (select min(PlayerNum) as p1, max(PlayerNum) as p2 from GAMES group by GameNum union select max(PlayerNum) as p1, min(PlayerNum) as p2 from GAMES group by GameNum) as q2
 group by q2.p2
 having count(distinct p1)=1
) as t2
where t1.p1=t2.p1 and t1.p2=t2.p2

http://sqlfiddle.com/#!2/4d9c4/9

主要想法:

  • 选择所有仅与1个其他玩家一起玩过的玩家1
  • 选择仅与另一位玩家一起玩过的所有玩家2

加入两组以找出只能互相比赛的球员

答案 1 :(得分:1)

with t as (
  select distinct min(playernum) a, max(playernum) b 
  from games group by gamenum
)
select x.a PlayerNum1, x.b PlayerNum2
from t x left join t y 
on not (x.a=y.a and x.b=y.b) and (y.a in (x.a,x.b) or y.b in (x.a,x.b))
where y.a is null

fiddle

答案 2 :(得分:0)

这是另一种对我来说更简单的方法。对于每个玩家,列出一个字符串中的所有游戏,然后找到对多个玩家完全相同的游戏列表:

select games, listagg(PlayerNum, ',') within group (order by PlayerNum)
from (select g.PlayerNum, listagg(GameNum, ',') within group (order by GameNum) as games
      from games g
      group by g.PlayerNum
     ) gp
group by games
having count(*) > 1;

SQL小提琴是here