首页 > 解决方案 > SQL 代码转换成单个 Django ORM 查询?

问题描述

我正在尝试将以下 SQL 查询转换为单个 Django 查询,以最大限度地减少执行的 sql 查询的数量,但是除了使用 for 循环之外我无法做到这一点。

如果有人能给我提供预期的 Django 查询,我将不胜感激

SQL查询:

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
)

楷模:

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)

标签: sqldjangopython-3.xdjango-models

解决方案


我们可以History用相关的最大值来注释对象Fund,例如:

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

或者更简单一点:

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

您可以通过以下方式稍微提高性能:

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]
    )
)

这将产生一个如下所示的查询:

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
)

以上与问题中的查询非常相似。

我们在这里获取History对象,这通常比掌握值本身要好,因为这是一种“原始痴迷”反模式。


推荐阅读