我正在开发一个项目,其中用户可以有一个个人资料,每个个人资料可以有多个帐户,一个帐户可以有多个付款。模型的重要部分如下:
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)
这段代码中的什么使子查询如此缓慢?我之前做过类似的子查询来获取子模型中的字段总和。我知道在孙子中执行此操作更复杂并且需要更多时间,但不应该那么慢。
有没有办法提高这个操作的效率?
由于以下问题,您遇到查询缓慢的问题
db_index=True
。Subqueries
可能有一些错误(缺少[:1])建议
ForeignKey
或 OneToOne
表示您在提到的模型之间的关系。它们隐含地 add 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)