Поиск победителя каждой группы игроков в турнире - PostgreSQL - PullRequest
4 голосов
/ 22 января 2020

Этот вопрос по PostgreSQL беспокоит меня уже довольно давно. Iv'e попробовал себя и искал везде, где мог, но не мог получить результат, который учитывает игрока из группы 3. Так что, хотя этот вопрос может быть дубликатом, правильный ответ не найден. Хотелось бы помочь.

вопрос заключается в следующем: Напишите запрос SQL, который возвращает таблицу, содержащую победителя в каждой группе. Каждая запись должна содержать идентификатор группы и идентификатор победителя в этой группе (игроки из одной группы соревнуются). Записи должны быть упорядочены путем увеличения идентификационного номера группы, и в случае ie выигрывает игрок с наименьшим идентификатором.

С учетом этой схемы:

игроков:

+-------------+-------+
| Column Name | Type  |
+-------------+-------+
| player_id   | int   |
| group_id    | int   |
+-------------+-------+

матчей:

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| match_id      | int     |
| first_player  | int     |
| second_player | int     | 
| first_score   | int     |
| second_score  | int     |
+---------------+---------+

Для следующего примера:

Таблица игроков:

+-----------+------------+
| player_id | group_id   |
+-----------+------------+
| 20        | 2          |
| 30        | 1          |
| 40        | 3          |
| 45        | 1          |
| 50        | 2          |
| 40        | 1          |
+-----------+------------+

Таблица матчей:

+------------+--------------+---------------+-------------+--------------+
| match_id   | first_player | second_player | first_score | second_score |
+------------+--------------+---------------+-------------+--------------+
| 1          | 30           | 45            | 10          | 12           |
| 2          | 20           | 50            | 5           | 5            |
| 3          | 65           | 45            | 10          | 10           |
| 4          | 30           | 65            | 3           | 15           |
| 5          | 45           | 65            | 8           | 4            |
+------------+--------------+---------------+-------------+--------------+

Ваш запрос должен вернуть:

+-----------+------------+------------+
| group_id  | winner_id  | tot_score  |
+-----------+------------+------------+ 
| 1         | 45         | 30         |
| 2         | 20         | 5          |
| 3         | 40         | 0          |
+-----------+------------+------------+

в группе 1 игрок 45 набрал наибольшее количество очков. во второй группе оба игрока набрали 5 очков, но у игрока 20 ID ниже, поэтому он победитель. в группе 3 только один игрок, и хотя он не сыграл ни одного матча, он является победителем.

лучшее, что удалось сделать до сих пор (на PostgreSQL):

SELECT group_id, player_id, score
FROM
(
    SELECT sq2.player_id, p.group_id, sq2.score, 
    RANK() OVER (PARTITION BY p.group_id ORDER BY score DESC) as position
    FROM
    (
      SELECT player_id, SUM(score) score
      FROM (
        SELECT first_player as player_id, first_score as score FROM matches
        UNION ALL
        SELECT second_player as player_id, second_score as score FROM matches
      ) as sq1
      GROUP BY player_id
    ) as sq2
    right join players p
    on p.player_id = sq2.player_id
) as sq3
WHERE position = 1 order by group_id, player_id

Что выводит это:

+-----------+-----------------------+------------+
| group_id  | player_id             | score      |
+-----------+-----------------------+------------+ 
| 1         | 45                    | 30         |
| 2         | 20                    | 5          |
| 2         | 50                    | 5          |
| 3         | [NULL](instead of 40) | [NULL] (should be 0)|
+-----------+-----------------------+------------+

Не могли бы вы помочь сгенерировать запрос с полным правильным результатом? (с информацией об игроке с группой 3)

Также хотелось бы знать, почему запрос возвращает NULL в player_id для правого соединения. Спасибо за помощь!

* этот вопрос, по-видимому, также является вопросом на Leetcode.com, который называется "Победители турниров" *

Ответы [ 2 ]

2 голосов
/ 22 января 2020

Вы можете использовать distinct on и агрегирование следующим образом:

select distinct on (p.group_id)
    p.group_id,
    player_id winner_id, 
    coalesce(sum(first_score) filter(where p.player_id = m.first_player), 0)
        + coalesce(sum(second_score) filter(where p.player_id = m.second_player), 0) 
        tot_score
from players p
left join matches m on p.player_id in (m.first_player, m.second_player)
group by p.group_id, p.player_id
order by p.group_id, tot_score desc, p.player_id

Это работает путем объединения игроков и матчей с условием in. Затем мы используем условную агрегацию, чтобы вычислить общий счет каждого игрока. Наконец, distinct on получает лучшего игрока в каждой группе.

Демонстрация на DB Fiddle :

group_id | winner_id | tot_score
-------: | --------: | --------:
       1 |        45 |        30
       2 |        20 |         5
       3 |        40 |         0

Примечание: спасибо за хорошо вопрос! ТАК можно использовать больше этого.

1 голос
/ 22 января 2020

Я собираюсь рекомендовать боковое соединение вместе с distinct on:

select distinct on (p.group_id) p.group_id, p.player_id, sum(v.win_score)
from players p left join
     (matches m left join lateral
      (values ((case when m.first_score > m.second_score then m.first_player else m.second_player end),
               (case when m.first_score > m.second_score then m.first_score else m.second_score end)
              )
      ) v(winner, win_score)
      on 1=1
     )
     on v.winner = p.player_id
group by p.group_id, p.player_id
order by p.group_id, sum(v.win_score) desc nulls last, p.player_id;

Здесь - это db <> скрипка

...