PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Berechnen Sie die maximale Summe eines annotierten Felds über eine gruppierte Abfrage in Django ORM?

Sie können kein Aggregat eines Aggregats Max(Sum()) erstellen , ist es in SQL nicht gültig, unabhängig davon, ob Sie das ORM verwenden oder nicht. Stattdessen müssen Sie die Tabelle mit sich selbst verbinden, um das Maximum zu finden. Sie können dies mit einer Unterabfrage tun. Der folgende Code sieht für mich richtig aus, aber denken Sie daran, dass ich nichts habe, womit ich ihn ausführen kann, also ist er vielleicht nicht perfekt.

from django.db.models import Subquery, OuterRef

annotation = {
    'AcSum': Sum('intensity')
}
# The basic query is on Relation grouped by A and Category, annotated
# with the Sum of intensity
query = Relation.objects.values('a', 'b__category').annotate(**annotation)

# The subquery is joined to the outerquery on the Category
sub_filter = Q(b__category=OuterRef('b__category'))
# The subquery is grouped by A and Category and annotated with the Sum
# of intensity, which is then ordered descending so that when a LIMIT 1
# is applied, you get the Max.
subquery = Relation.objects.filter(sub_filter).values(
    'a', 'b__category').annotate(**annotation).order_by(
    '-AcSum').values('AcSum')[:1]

query = query.annotate(max_intensity=Subquery(subquery))

Dies sollte SQL wie folgt generieren:

SELECT a_id, category_id,
       (SELECT SUM(U0.intensity) AS AcSum
        FROM RELATION U0
        JOIN B U1 on U0.b_id = U1.id
        WHERE U1.category_id = B.category_id
        GROUP BY U0.a_id, U1.category_id
        ORDER BY SUM(U0.intensity) DESC
        LIMIT 1
       ) AS max_intensity
FROM Relation
JOIN B on Relation.b_id = B.id
GROUP BY Relation.a_id, B.category_id

Es kann performanter sein, den Join in Subquery zu eliminieren durch Verwendung einer Backend-spezifischen Funktion wie array_agg (Postgres) oder GroupConcat (MySQL), um die Relation.ids zu sammeln die in der äußeren Abfrage zusammengefasst sind. Aber ich weiß nicht, welches Backend Sie verwenden.