Reputation: 1455
Here is my schema:
City --> Photographer
I'm trying to get a list of cities that have at least one photographer, and return the photographer count for the cities.
Here is the queryset I'm working with:
City.objects.annotate(photographer_count=aggregates.Count('photographers')).filter(photographer_count__gt=0).order_by('-photographer_count')
This works exactly as I would expect it too, except for some reason Django chooses to make the join between city/photographer with a left outer join. If I grab the SQL text and simply change the "left outer" to "inner", the query goes from ~11 seconds to 200ms with identical results.
I've tried putting a filter in front of the annotate to hint to Django that it should be inner joining, but that didn't work.
Any Django query voodoo I can perform on this to get that inner join? I realize I can use straight SQL, but would prefer to go through the ORM.
Upvotes: 6
Views: 5636
Reputation: 364
By default, a LEFT JOIN
is generated so that Django can get you rows even for cities with zero photographers. If you know you don't want those, here's a trick to force Django to generate an INNER JOIN
:
City.objects.filter(
photographer__isnull=False
).annotate(
photographer_count=aggregates.Count('photographers')
).filter(
photographer_count__gt=0
).order_by(
'-photographer_count'
)
Specifically, that first filter tells Django that an INNER JOIN
is safe. It must come before the annotate()
call.
Upvotes: 20
Reputation: 6576
You generally can't get this level of control over the queries and joins that Django executes.
Django doesn't do really clever things like notice that the filter condition means you can get away with an INNER JOIN at this point - it has to produce queries that are correct in the general case, which in this case means a LEFT OUTER JOIN as far as I can tell.
So you are probably going to need raw SQL.
Upvotes: 0