Django 嵌套子查询速度极慢

问题描述 投票:0回答:1

我正在开发一个项目,其中用户可以有一个个人资料,每个个人资料可以有多个帐户,一个帐户可以有多个付款。模型的重要部分如下:

class Profile(models.Model):
    username = models.CharField(max_length=250)

class Account(models.Model):
    profile_id = models.PositiveIntegerField()

class Payment(models.Model):
    amount = models.DecimalField(max_digits=8, decimal_places=2)
    account_id = models.CharField() # An unique id for the account the payment belongs
  

我必须在 Profile 模型中创建一个注释,其中包含该用户所有付款金额的总和,以便随后使用 pandas 操作该数据。我设法使用嵌套子查询创建一个查询集来完成工作,但此操作非常慢(比迭代所有配置文件来计算此值慢)。

我是这样做的:

payment_groups = Payment.objects.filter(account_id=OuterRef("pk")).order_by().values("account_id")

payments_total = Subquery(payment_groups.annotate(total=Sum("amount")).values("total"), output_field=models.DecimalField())

account_groups = Account.objects.filter(profile_id=OuterRef("pk")).order_by().values("profile_id")

accounts_total = Subquery(account_groups.annotate(total=Sum(paymments_total)).values("total"), output_field=models.DecimalField())

profiles = Profile.objects.all().annotate(account_total=acount_total)

这段代码中的什么使子查询如此缓慢?我之前做过类似的子查询来获取子模型中的字段总和。我知道在孙子中执行此操作更复杂并且需要更多时间,但不应该那么慢。

有没有办法提高这个操作的效率?

python python-3.x django
1个回答
0
投票

由于以下问题,您遇到查询缓慢的问题

  1. 您对相关模型使用了错误的字段类型并明确 您没有添加
    db_index=True
  2. 您的
    Subqueries
    可能有一些错误(缺少[:1])

建议

  1. 使用
    ForeignKey
    OneToOne
    表示您在提到的模型之间的关系。它们隐含地
    add db_index=True
    在您的数据库中
  2. 在某些情况下,您可以在其他不相关的内容上添加 db_index=True 模型字段以加速查询

这是一个例子

from django.db import models


class Profile(models.Model):
    username = models.CharField(max_length=250)


class Account(models.Model):
    profile_id = models.ForeignKey(
        to=Profile, null=False, blank=True, on_delete=models.CASCADE
    )


class Payment(models.Model):
    amount = models.DecimalField(max_digits=8, decimal_places=2)
    account = models.ForeignKey(
        to=Account, null=False, blnak=True, on_delete=models.CASCADE
    )


payment_groups = (
    Payment.objects.filter(account_id=models.OuterRef("pk"))
    .order_by()
    .values("account_id")
)
payments_total = models.Subquery(
    payment_groups.annotate(total=models.Sum("amount")).values("total")[:1],
    output_field=models.DecimalField(),
)
account_groups = (
    Account.objects.filter(profile_id=models.OuterRef("pk"))
    .order_by()
    .values("profile_id")
)
accounts_total = models.Subquery(
    account_groups.annotate(total=models.Sum(payments_total)).values("total")[:1],
    output_field=models.DecimalField(),
)
profiles = Profile.objects.all().annotate(account_total=accounts_total)

© www.soinside.com 2019 - 2024. All rights reserved.