Speed up Django Querysets with RawSQL
I’m writing this because the documentation makes it seem pretty scary, but it has saved me a couple of times.
Today, I wanted to filter a queryset based on average of a value from a related model. The average had to be grouped by something too. In SQL that would be something like...
``` SELECT something.*, AVG(relatedthing.att) AS avg_att FROM something JOIN relatedthing ON relatedthing.s = something.id HAVING avg_att > 3 GROUP BY something.thing ```
But, I'm determined to use the Django ORM. So, I started out with
``` queryset = SomeThings.objects.prefetch_related( "relatedthings_set" ).Values( "thing" ).annotate( avg_att=Avg("relatedthings__attribute) ).filter( avg_att__gte=3 ) ```
But, SomeThings is a large set of objects, several thousand objects, and RelatedThings is also a large dataset. There are like a hundred RelatedThings for every SomeThing. So this took a long time to return and I could see Django was doing lots of strange queries in the console...
I then remembered about the RawSQL annotation.
``` queryset = SomeThings.objects.annotate( avg_att=RawSQL( """ SELECT AVG(relatedthing.att) FROM relatedthing WHERE relatedthing.s = something.id """, params=[] ) ).filter( avg_att__gte=3 ) ```
And this ran much faster. It created a query like, but probably uglier,
``` SELECT somethings.id, somethings.x, somethings.s ( SELECT AVG(relatedthing.att) FROM relatedthing WHERE relatedthing.s = something.id ) AS avg_att FROM somethings HAVING avg_aat >= 3 ```












