使用分组依据无法在两个表中获取最大值

时间:2019-03-02 19:02:05

标签: mysql sql postgresql

很长一段时间以来,我一直在努力解决问题,但是我没有取得任何进展。基本上,我有两个表playersmatchesplayers中的每个玩家都有一个唯一的player_id,以及一个group_id来标识他/她属于哪个组。 matches中的每场比赛都有两名球员player_idfirst_player中的second_player人,他们总是来自同一组。 first_score对应于first_player得分的分数,second_score对应于second_player得分的分数。得分更高的人赢得比赛。这是两个表:

create table players (
      player_id integer not null unique,
      group_id integer not null
);

create table matches (
      match_id integer not null unique,
      first_player integer not null,
      second_player integer not null,
      first_score integer not null,
      second_score integer not null
);

现在我想做的是让每个小组中获胜最多的球员,他们的小组ID和获胜次数。因此,例如,如果有三个组,结果将类似于:

Group    Player    Wins
1        24        23
2        13        25
3        34        20

这就是我现在拥有的

SELECT p1.group_id AS Group, p1.player_id AS Player, COUNT(*) AS Wins
FROM players p1, matches m1
WHERE (m1.first_player = p1.player_id AND m1.first_score > m1.second_score) 
   OR (m1.second_player = p1.player_id AND m1.second_score > m1.first_score)
GROUP BY p1.group_id
HAVING COUNT(*) >= (
            SELECT COUNT(*)
            FROM players p2, matches m2
            WHERE p2.group_id = p1.group_id AND
                ((m2.first_player = p2.player_id AND m2.first_score > m2.second_score) 
                   OR (m2.second_player = p2.player_id AND m2.second_score > m2.first_score))
)

我的想法是仅选择获胜大于或等于该组中所有其他玩家的获胜者。我的查询存在一些语法问题。我想我也错误地使用了GROUP BY。

在获胜次数上也存在平局的问题,我应该只让player_id最少的玩家获胜。但是我还没到那个地步。非常感谢您的帮助,谢谢!

编辑1

我有一些样本数据用于运行查询。

SELECT * FROM players给了我这个:

Player_ID Group_ID
100        1
200        1
300        1
400        2
500        2
600        3
700        3

SELECT * FROM matches给了我这个:

match_id first_player second_player first_score second_score
1        100          200           10          20
2        200          300           30          20
3        400          500           30          10
4        500          400           20          20
5        600          700           20          10

因此,查询应返回:

Group    Player    Wins
1        200       2
2        400       1
3        600       1

按原样运行查询将返回以下错误:

ERROR:  column "p1.player_id" must appear in the GROUP BY clause or be used in an aggregate function

现在我了解,如果我想在SELECT(或HAVING)语句中使用player_id,则必须在GROUP BY子句中指定它,但我不希望按玩家ID分组,只能按组ID。

即使我确实在外部查询中将p1.player_id添加到GROUP BY,也实际上得到了正确的答案。但是我有点困惑。分组依据是否不根据该列汇总表?从逻辑上讲,我只想按p1.group_id分组。

此外,如果我要在一组中拥有最多获胜次数最多的玩家,我该如何保持player_id最少的玩家?

编辑2

如果我将matches表更改为第1组,则有两名玩家各赢1个,则查询结果将从结果中完全忽略第1组。 因此,如果我的matches表是:

match_id first_player second_player first_score second_score
1        100          200           10          20
2        200          300           10*         20
3        400          500           30          10
4        500          400           20          20
5        600          700           20          10

我希望结果是

Group    Player    Wins
1        200       1
1        300       1
2        400       1
3        600       1

但是,我得到以下信息:

Group    Player    Wins
2        400       1
3        600       1

请注意,所需结果是

Group    Player    Wins
1        200       1
2        400       1
3        600       1

因为我希望在平局时只选择player_id最少的玩家。

3 个答案:

答案 0 :(得分:0)

尝试如下

with cte as
   ( 
  select p.Group_ID,t1.winplayer,t1.numberofwin
row_number()over(partition by p.Group_ID order by t1.numberofwin desc,t1.winplayer) rn  from players p join  
    (
     SELECT count(*) as numberofwin,
      case when first_score >second_score then first_player 
      else second_player end as winplayer
      FROM matches group by case when first_score >second_score then first_player 
      else second_player end
    ) t1 on p.Player_ID =t1.winplayer
 ) select * from cte where rn=1

答案 1 :(得分:0)

当您在GROUP BY中添加player_id时,此方法有效,因为您知道每个玩家仅在一个组中玩。因此,您可以按玩家分组。因此,从逻辑上讲,您可以将player_id添加到GROUP BY。

答案 2 :(得分:0)

WITH first_players AS (
    SELECT group_id,player_id,SUM(first_score) AS scores FROM players p LEFT JOIN matches m ON p.player_id=m.first_player GROUP BY group_id,player_id
    ),
    second_players AS (
    SELECT group_id,player_id,SUM(second_score) AS scores FROM players p LEFT JOIN matches m ON p.player_id=m.second_player GROUP BY group_id,player_id
    ),
    all_players AS (
        WITH al AS (
            SELECT group_id, player_id, scores FROM first_players 
            UNION ALL
            SELECT group_id, player_id, scores FROM second_players
        )
        SELECT group_id, player_id,COALESCE(SUM(scores),0) AS scores FROM al GROUP BY group_id, player_id 
    ),
    players_rank AS (
        SELECT *, 
        ROW_NUMBER() OVER(PARTITION BY group_id ORDER BY scores DESC, player_id ASC) AS score_rank,
        ROW_NUMBER() OVER(PARTITION BY scores ORDER BY player_id ASC) AS id_rank FROM all_players ORDER BY group_id
    )
    SELECT group_id, player_id AS winner_id FROM players_rank WHERE score_rank=1 AND id_rank=1

结果

   group_id winner_id
    1   45
    2   20
    3   40

Try it Out