This Django ORM statement:
Model.objects.all()
.annotate(
ord=Window(
expression=RowNumber(),
partition_by=F('related_id'),
order_by=[F("date_created").desc()]
)
)
.filter(ord=1)
.filter(date_created__lte=some_datetime)
Leads to the following SQL query:
SELECT *
FROM (
SELECT
id, related_id, values, date_created
ROW_NUMBER() OVER (
PARTITION BY related_id
ORDER BY date_created DESC
) AS ord
FROM model_table
WHERE date_created <= 2022-02-24 00:00:00+00:00
)
WHERE ord = 1
As you can see, the date_created__lte
filter gets applied on the inner query. Is it possible to control statement location preciser and move the filter outside, like ord
?
2
Answers
I found a way to achieve the results I want: applying a filter,
date_created__lte
in this example, outside of annotated query.However, this is not a code I want, it's bad from performance point of view due to HASH JOIN. Of course, I can write a raw SQL query, parse values by Django ORM, but this looks too heavy for a simple nested subquery. So, a better answer is appreciated 🙏
In your case, you can move the date_created__lte filter before the annotate method to make sure it is applied to the base query. This will improve the query performance too because the annotate method works on a smaller, filtered dataset.