1

I'm trying to convert the following SQL query in to a single Django query in order to minimize number of executed sql queries, however I'm not able to do that except using a for-loop.

I appreciate it if anyone could provide me the intended Django query

SQL Query:

select FUND_ID, REPORT_DATE, PURCHASE_NAV_PER_SHARE
FROM FUNDS_HISTORY
WHERE REPORT_DATE = (
   SELECT Max(REPORT_DATE)
   FROM FUNDS_HISTORY fund_history
   where fund_history.FUND_ID = FUNDS_HISTORY.FUND_ID
)

models:

class Fund(models.Model):
    name_en = models.CharField(max_length=256, blank=True, null=True)


class History(models.Model):
    fund = models.ForeignKey('funds.Fund', on_delete=models.CASCADE, blank=True, null=True)
    purchase_nav_per_share = models.BigIntegerField(blank=True, null=True)
    report_date = models.DateField(blank=True, null=True)

2
  • Please share the related models. Commented Aug 5, 2019 at 12:40
  • please edit your question. Commented Aug 5, 2019 at 12:46

2 Answers 2

1

We can annotate the History objects with the maximum of the related Fund, like:

History.objects.annotate(
    max_date=Max('fund__history__report_date')
).filter(
    report_date=F('max_date')
)

or a bit simpler:

History.objects.filter(
    report_date=Max('fund__history__report_date')
)

You can slightly improve performance with:

from django.db.models import Max, OuterRef, Subquery

History.objects.filter(
    report_date=Subquery(
        History.objects.filter(
            fund_id=OuterRef('fund_id')
        ).values('fund_id').annotate(
            last_date=Max('report_date')
        ).values('last_date')[:1]
    )
)

This will result in a query that looks like:

SELECT history.id, history.fund_id, history.purchase_nav_per_share, history.report_date
FROM history
WHERE history.report_date = (
    SELECT MAX(U0.report_date) AS last_date
    FROM history U0
    WHERE U0.fund_id = history.fund_id
    GROUP BY U0.fund_id
    LIMIT 1
)

The above is quite similar to the query in the question.

We here obtain the History objects, which is usually better than grasping the values itself since that is a sort of "primitive obsession" antipattern.

Sign up to request clarification or add additional context in comments.

8 Comments

actually I want the value of purchase_nav_per_share related to the maximum report date for every fund_id.
@Erfan: but you get that: the History objects from this queryset of course have a .purchase_nav_per_share attribute.
the translation of your suggestion is a complex SQL query and I couldn't get the output according to its high execution time
@Erfan: it has two JOINs: one is on the fund model, which is always a single one, then another history, which will again result in some extra values. But in general both the queries in the question and answer should run in O(n^2) with n the number of Historys.
I'm not sure but Django may translating it in an improper way which is the following sql code. I am using django debug toolbars to retrieve sql query
|
0

You can translate above sql query as django ORM.

History.objects.annotate(report_date_max=Max('report_date')).filter(report_date=F('report_date_max')).values_list('fund_id', 'report_date', 'purchase_nav_per_share')

Model field names might be incorrect as you have not shared related field names.

2 Comments

thanks for response. your query seems to be same as following sql but I wan't the query returning one record per fund id while your suggestion does not provide my desirable output. SELECT "funds_history"."fund_id", "funds_history"."report_date", "funds_history"."purchase_nav_per_share" FROM "funds_history" GROUP BY "funds_history"."id" ORDER BY "funds_history"."id" ASC
try this History.objects.values_list('fund_id').annotate(report_date_max=Max('report_date')).filter(report_date=F('report_date_max')).values_list('fund_id', 'report_date', 'purchase_nav_per_share')

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.