Eu tenho uma mesa (jogadores) contendo uma lista de jogadores, gostaria de emparelhar esses jogadores de uma maneira única, para que o oponente de cada jogador seja único a cada rodada (ou seja, toda vez que a consulta SELECT for chamada).
A player_opponent_log
coluna integer[]
contém os player_id
s dos jogadores que jogaram com aquele jogador em uma rodada anterior (e é usada para ajudar a escolher jogadores únicos). Esta coluna é preenchida posteriormente usando os resultados da consulta SELECT - e está fora do escopo desta questão.
A tabela por exemplo teria os seguintes dados;
player_id | player_opponent_log
-----------+---------------------
1 | {2,3}
2 | {1}
3 | {1}
4 | {}
5 | {}
6 | {}
7 | {8}
8 | {7}
O que estou tentando alcançar seria algo ao longo das seguintes linhas:
player_id1 | player_id2
------------+------------
1 | 4
2 | 3
5 | 7
6 | 8
Eu tentei inúmeras abordagens diferentes, incluindo GROUP BY
, DISTINCT ON ()
, self JOINS, mas não consegui chegar a uma solução funcional.
O seguinte resultado é o que estou obtendo atualmente e estou tentando evitar:
player_id1 | player_id2
------------+------------
1 | 4
2 | 3
3 | 4
4 | 1
5 | 1
6 | 1
7 | 1
8 | 1
Onde estou ficando preso é como eliminar um único jogador sendo alocado para uma rodada mais de uma vez por consulta SELECT.
Qualquer ideia sobre como resolver isso seria muito apreciada.
Cada linha do resultado depende da linha anterior. Um
CTE recursivovem à mente, eu tentei isso. Mas seria necessário referir-se à tabela de trabalho em umaOUTER JOIN
ou uma expressão de subconsulta que não é permitida. Isso não funciona (com base no layout da tabela no meu violino ):Não acho que haja uma maneira meio decente de resolver isso com SQL puro. Eu sugiro:
Solução processual com PL/pgSQL
Como?
Construa uma tabela temporária com os pares possíveis restantes. Esse tipo de junção cruzada produz muitas linhas com mesas grandes, mas como parece que estamos falando de torneios, os números devem ser razoavelmente baixos.
Os jogadores com a lista mais longa de oponentes são classificados primeiro. Dessa forma, os jogadores que seriam difíceis de igualar vêm primeiro, aumentando a chance de uma solução.
Escolha a primeira linha e exclua os pares relacionados agora obsoletos. Precisa classificar novamente. Logicamente qualquer linha é boa, praticamente pegamos o jogador com a lista mais longa de oponentes primeiro devido à classificação inicial (que não é confiável sem
ORDER BY
, mas boa o suficiente para o caso).Repita até não sobrar nenhuma correspondência.
Mantenha a contagem e gere uma exceção se a contagem não for a esperada. O PL/pgSQL permite convenientemente lançar uma exceção após o fato, que cancela qualquer valor de retorno anterior. Detalhes no manual.
Ligar:
Resultado:
SQL Fiddle.
Observação
Isso não garante o cálculo da solução perfeita. Eu apenas devolvo uma solução possível e uso alguma inteligência limitada para melhorar as chances de encontrar uma. O problema é um pouco como resolver um Sudoku , realmente e não trivialmente resolvido perfeitamente .