sql >> Base de Datos >  >> RDS >> PostgreSQL

Forma correcta de anotar un campo de rango para un conjunto de consultas

Lamentablemente, no es una operación posible ya que (para mí) postgresql WHERE La operación (filtrar/excluir) reduce las filas antes de que las funciones de agregación puedan trabajar en ellas.

La única solución que encontré es simplemente calcular la clasificación para todas las Person con un conjunto de consultas separado y luego, para anotar su conjunto de consultas con estos resultados.

Esta respuesta (consulte el método mejorado) explica cómo "anotar un conjunto de consultas con datos preparados externamente en un dictado".

Aquí está la implementación que hice para sus 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')))

Lo probé con Django 2.1.3 y Postgresql 10.5, por lo que el código puede cambiar ligeramente para usted.
¡Siéntase libre de compartir una versión compatible con Django 1.11!