Reputation: 437
I have tables players and matches and I want to find players with max points, note that player with lower id is winner in each group if scores matches.
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
);
insert into players values(20, 2);
insert into players values(30, 1);
insert into players values(40, 3);
insert into players values(45, 1);
insert into players values(50, 2);
insert into players values(65, 1);
insert into matches values(1, 30, 45, 10, 12);
insert into matches values(2, 20, 50, 5, 5);
insert into matches values(13, 65, 45, 10, 10);
insert into matches values(5, 30, 65, 3, 15);
insert into matches values(42, 45, 65, 8, 4);
Now I want result as
Note that first and second player can be same from group.
Result:-
group_id | winner_id
----------+-----------
1 | 45
2 | 20
3 | 40
I am unsure how to proceed.
Upvotes: 0
Views: 4485
Reputation: 23
This is the right answer all the above just give 90% from the needed you can check it here
https://www.db-fiddle.com/f/2Lp3LoMpXVefUbezRKBY3N/0
select group_id,player_id from
(
select
group_id
,P.player_id
,sum(points) as total
,DENSE_RANK () OVER (
PARTITION BY group_id
ORDER BY sum(points) desc, P.player_id asc
)P_Rank
from
(
select c.match_id, t.*
from matches c
cross join lateral (
values
(c.first_player, c.first_score),
(c.second_player, c.second_score)
) as t(player, points)
order by match_id, points
) base
right join players P
on base.player = P.player_id
group by 1,2
order by 1
)BT
where P_Rank=1
Upvotes: 0
Reputation: 1
SELECT GROUP_ID, PLAYER_ID, MAX_SCORE FROM (
SELECT GROUP_ID, PLAYER_ID, MAX_SCORE, DENSE_RANK() OVER(PARTITION BY GROUP_ID ORDER BY MAX_SCORE DESC,PLAYER_ID ASC) RANK
FROM
(
SELECT DISTINCT GROUP_ID, PLAYER_ID, COALESCE(MAX(TOT_SCORE)OVER (PARTITION BY PLAYER_ID),0)MAX_SCORE
FROM
(
SELECT PLAYER_ID, GROUP_ID, TOT_SCORE FROM
(
SELECT PLAYER, SUM(SCORE) AS TOT_SCORE
FROM
(
SELECT FIRST_PLAYER AS PLAYER,SUM(FIRST_SCORE)AS SCORE FROM MATCHES GROUP BY FIRST_PLAYER
UNION
SELECT SECOND_PLAYER AS PLAYER,SUM(SECOND_SCORE)AS SCORE FROM MATCHES GROUP BY SECOND_PLAYER
)
GROUP BY PLAYER
)S
RIGHT JOIN
PLAYERS P
ON
S.PLAYER=P.PLAYER_ID
))) WHERE RANK=1
Upvotes: 0
Reputation: 21
Approach taken:
For table 2 we will be getting the winner and corresponding score for each player and then we will group the players to calculate the overall sum
After this above table will be joined with table 1 which will give the score for each player in the groups, we will sort this data based on group and score
Then finally we will select the first record of each group which will give the required results
with actual_data as
(select row_number()over(partition by group_id
order by group_id asc,
coalesce(table2.sum_score, 0) desc) as rn,
group_id, player_id, coalesce(table2.sum_score, 0) as score
from players as table1
left join
(select player, sum(score) as sum_score from (
select match_id,
case
when first_score > second_score then first_player
when first_score = second_score and first_player < second_player
then first_player
else second_player end as player,
case
when first_score > second_score then first_score
when first_score = second_score and first_player < second_player
then first_score
else second_score end as score
from matches) as foo
group by player
order by sum_score desc) as table2
on table1.player_id = table2.player
order by group_id asc, coalesce(table2.sum_score, 0) desc
)
select group_id , player_id from actual_data where rn = 1
Upvotes: 2
Reputation: 1269703
I think the simplest approach is a lateral join and distinct on
:
select distinct on (p.group_id) p.group_id, v.player, v.score
from matches m cross join lateral
(values (first_player, first_score), (second_player, second_score)
) v(player, score) join
players p
on v.player = p.player_id
order by p.group_id, v.score desc;
Here is a db<>fiddle.
Upvotes: 2
Reputation: 222442
Use row_number()
:
select group_id, player_id
from (
select
p.*,
row_number() over(
partition by p.group_id
order by case
when m.first_player = p.player_id then m.first_score
else m.second_score
end desc,
player_id
) rn
from players p
inner join matches m
on m.first_player = p.player_id or m.second_player = p.player_id
) x
where rn = 1
| group_id | player_id |
| -------- | --------- |
| 1 | 65 |
| 2 | 20 |
Note: there is just one player in group 3 (player_id 40), and they didn't participate any game.
Upvotes: 2