I am trying to run an aggregation query that is roughly equals to:
select
sum(impressions) as impressions,
sum(clicks) as clicks,
sum(clicks)/sum(impressions) as ctr
from stats
group by product
order by ctr;
The database used is PostgreSQL.
I made this query expression (Django 1.9):
Stats.objects.values('product').annotate(
impressions = models.Sum('impressions'),
clicks = models.Sum('clicks'),
ctr = models.ExpressionWrapper(
models.F('clicks')/models.F('impressions')), output_field = models.FloatField()
)
).order_by('ctr')
There are 2 problems with it:
ctr
is 0.0 because it divides integers at the database leveldivision by zero
if impressions are 0What is the proper solution?
In the Django QuerySet API, F() expressions are used to refer to model field values directly in the database.
Definition and Usage. The exact lookup is used to get records with a specified value. The exact lookup is case sensitive. For a case insensitive search, use the iexact lookup.
Django offers a QuerySet method called select_related() that allows you to retrieve related objects for one-to-many relationships. This translates to a single, more complex QuerySet, but you avoid additional queries when accessing the related objects. The select_related method is for ForeignKey and OneToOne fields.
Subquery() expressions. You can add an explicit subquery to a QuerySet using the Subquery expression. The examples in this section are designed to show how to force Django to execute a subquery. In some cases it may be possible to write an equivalent queryset that performs the same task more clearly or efficiently.
Use conditional expressions:
from django.db.models import Case, F, Sum, When
Stats.objects.values('product').annotate(
tot_impressions=Sum('impressions'),
tot_clicks=Sum('clicks')
).annotate(
ctr=Case(When(tot_impressions=0, then=None), # or other value, e.g. then=0
# 1.0*... is to get float in SQL
default=1.0*F('tot_clicks')/F('tot_impressions'),
output_field=models.FloatField())
).order_by('ctr')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With