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

Calcolare il massimo della somma di un campo annotato su una query raggruppata in Django ORM?

Non puoi fare un aggregato di un aggregato Max(Sum()) , non è valido in SQL, indipendentemente dal fatto che tu stia utilizzando l'ORM o meno. Invece, devi unire il tavolo a se stesso per trovare il massimo. Puoi farlo usando una sottoquery. Il codice seguente mi sembra giusto, ma tieni presente che non ho qualcosa su cui eseguirlo, quindi potrebbe non essere perfetto.

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

Questo dovrebbe generare SQL come:

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

Potrebbe essere più efficace eliminare il join in Subquery utilizzando una funzione specifica del back-end come array_agg (Postgres) o GroupConcat (MySQL) per raccogliere il Relation.ids raggruppati nella query esterna. Ma non so quale backend stai usando.