PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Maneira correta de anotar um campo de classificação para um conjunto de consultas


Infelizmente, não é uma operação possível, pois (para mim) o postgresql WHERE A operação (filtrar/excluir) restringe as linhas antes que as funções de agregação possam trabalhar nelas.

A única solução que encontrei é simplesmente calcular a classificação para todas as Person com um conjunto de consultas separado e, em seguida, anotar seu conjunto de consultas com esses resultados.

Esta resposta (veja o método aprimorado) explica como "anotar um conjunto de consultas com dados preparados externamente em um dict".

Aqui está a implementação que fiz para seus modelos:
class PersonQuerySet(models.QuerySet):
    def total_scores(self):
        # compute the global ranking
        ranks = (Person.objects
                 .annotate(total_score=models.Sum('session__gamesession__score'))
                 .annotate(rank=models.Window(expression=DenseRank(),
                                              order_by=models.F('total_score').decs()))
                 .values('pk', 'rank'))
        # extract and put ranks in a dict
        rank_dict = dict((e['pk'], e['rank']) for e in ranks)

        # create `WHEN` conditions for mapping filtered Persons to their Rank
        whens = [models.When(pk=pk, then=rank) for pk, rank in rank_dict.items()]
        # build the query
        return (self.annotate(rank=models.Case(*whens, default=0,
                                               output_field=models.IntegerField()))
                .annotate(total_score=models.Sum('session__gamesession__score')))

Eu testei com Django 2.1.3 e Postgresql 10.5, então o código pode mudar levemente para você.
Sinta-se à vontade para compartilhar uma versão compatível com o Django 1.11!