PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Modo corretto per annotare un campo di classificazione per un set di query

Purtroppo, non è un'operazione possibile poiché (per me) il postgresql WHERE operazione (filtra/escludi) restringe le righe prima che le funzioni di aggregazione possano utilizzarle.

L'unica soluzione che ho trovato è calcolare semplicemente la classifica per tutte le Person con un set di query separato e quindi, per annotare il set di query con questi risultati.

Questa risposta (vedi il metodo migliorato) spiega come "annotare un set di query con dati preparati esternamente in un dict".

Ecco l'implementazione che ho realizzato per i tuoi modelli:

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')))

L'ho testato con Django 2.1.3 e Postgresql 10.5, quindi il codice potrebbe cambiare leggermente per te.
Sentiti libero di condividere una versione compatibile con Django 1.11!