Classify by Average after Sum

Hi,

I’m trying to create a query that will find how much each customer spend in purchases, and then classify the customers by their average purchases sum.

annotate return an error and aggregate return a dictionary.
My initial query was:

c=Customer.objects.annotate(
    total_sum=Sum(F('salesorder__salesorderitems__quantity')*F('salesorder__salesorderitems__prod_id__unit_price'), output_field=FloatField()))\
    .values('id','fname','lname','total_sum').order_by('-total_sum')\
    .annotate(level=Case(When(Q(total_sum__gt=Avg('total_sum')) ,then="good"),\
                          When(Q(total_sum__lte=Avg('total_sum')), then="bad"),\
                          default="None"))

I think I can do it with two DB hits:
One to calculate the Average and one to do the classification, but trying to avoid two DB hits.

Can someone clarify how to achieve my goal ?

I think you need to do the aggregation functions (e.g. Avg) within a subquery.

See the docs and example in Using aggregates within a Subquery expression. I think this is pretty close to what you’re trying to do.

1 Like

I think I figure it out:

customers = Customer.objects.filter(id=OuterRef('pk'))

customers_total = customers.annotate(total_sum=Sum(F('salesorder__salesorderitems__quantity')*F('salesorder__salesorderitems__prod_id__unit_price'), output_field=FloatField())).values('total_sum')

c=Customer.objects.annotate(
    total_sum=Sum(F('salesorder__salesorderitems__quantity')*F('salesorder__salesorderitems__prod_id__unit_price'), output_field=FloatField()))\
    .values('id','fname','lname','total_sum').order_by('-total_sum')\
    .annotate(level=Case(When(Q(total_sum__gt=Avg(Subquery(customers_total))) ,then=Value("prefered client")),\
                          When(Q(total_sum__lte=Avg(Subquery(customers_total))), then=Value("regular client")),\
                          default=Value("None"), output_field=CharField()))

Not really sure, but connection.queries shows only one db hit.

1 Like