PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Manière appropriée d'annoter un champ de classement pour un ensemble de requêtes

Malheureusement, ce n'est pas une opération possible puisque (pour moi) le postgresql WHERE l'opération (filtrer/exclure) réduit les lignes avant que les fonctions d'agrégation ne puissent les utiliser.

La seule solution que j'ai trouvée est de simplement calculer le classement de tous les Person avec un jeu de requêtes séparé, puis annoter votre jeu de requêtes avec ces résultats.

Cette réponse (voir la méthode améliorée) explique comment "annoter un jeu de requêtes avec des données préparées en externe dans un dict".

Voici l'implémentation que j'ai faite pour vos modèles :

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

Je l'ai testé avec Django 2.1.3 et Postgresql 10.5, donc le code peut légèrement changer pour vous.
N'hésitez pas à partager une version compatible avec Django 1.11 !