Django ORM注释Sum计算错误及其乘以条目数,它是有线的

Posted

技术标签:

【中文标题】Django ORM注释Sum计算错误及其乘以条目数,它是有线的【英文标题】:Django ORM annotate Sum calculation wrong and its multiplying by number of entry, it's wired 【发布时间】:2020-09-09 14:34:46 【问题描述】:

我正在经历 Django ORM 中的有线情况。它返回了错误的总和计算,并且意外地乘以我不想要的条目数,它的行为完全是有线的。

这些是我的模型

class Person(models.Model):
    name = models.CharField(max_length=100)



class Purchase(models.Model):
    person = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name='person_purchase'
    )

    amount = models.DecimalField(decimal_places=2, max_digits=5)



class Consumption(models.Model):
    person = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name='person_consumption'
    )

    amount = models.DecimalField(decimal_places=2, max_digits=5)

这是我的查询:

person_wise_payable = Person.objects.annotate(
            difference=ExpressionWrapper(
                Coalesce(Sum('person_purchase__amount'), Value(0)) - Coalesce(Sum('person_consumption__amount'), Value(0)),
                output_field=FloatField()
            ),
        )

    for i in person_wise_payable:
        print(i.difference, i.name)

我试图找出个人明智购买和消费之间的区别。

例如,我有 3 个人,foo, doe, jhon

这些是他们的参赛作品

Purchase models entries:
foo = 5
doe = 2
doe = 3
doe = 3


Consumption models entries:

foo = 1
foo = 2
foo = 2
doe = 1
doe = 1
jhon = 2

如你所见,

foo total purchase is 5
doe total purchase is 8
jhon total purchase is 0 (coz, no entry of him)

foo total consumption is 5
doe total consumption is 2
jhon total consumption is 2

如果我们从purchase 中减去consumption,那么预期的输出/差异

foo : 5 - 5 =   0
doe: 8 - 2 =   6
jhon: 0 - 2 =   -2

我希望你明白我想要做什么以及预期的输出。

foo 0, doe 6 and jhon -2

但问题是,我当前的查询没有像上面这样返回输出,它返回的结果非常连线,请参见下面的连线结果。

-2.0 jhon
10.0 doe
10.0 foo

谁能帮助我如何正确完成它?最近几天我一直在苦苦挣扎,还没有做到这一点

【问题讨论】:

如果您进行多个 JOIN,它们将充当乘数,因为您进行查询 SELECT SUM(some_col) FROM some_table LEFT OUTER JOIN other_table LEFT OUTER JOIN yet_another_table,所以 some_col 会重复。 究竟为什么首先需要两个模型?也许您可以制作一种模型,其中购买金额为正,消费金额为负? 我想保留两个模型用于购买和消费,因为消费在我的业务逻辑中也有不同的用例,无论如何我想要两个模型 我认为你的意思是 WEIRD 而不是 WIRED 哈哈。 【参考方案1】:

如果您进行多个 JOIN,它们将充当乘数,因为您进行了查询

SELECT SUM(purchase.amount) - SUM(consumption.amount)
FROM person
LEFT OUTER JOIN purchase
LEFT OUTER JOIN consumption

所以 same purchase.amount 重复了很多次,因为有相关的consumptions,并且 same consumption.amount 重复了很多次相关purchases。

您可以使用子查询来解决这个问题,例如:

person_wise_payable = Person.objects.annotate(
    difference=Coalesce(Subquery(
        Purchase.objects.filter(
            person=OuterRef('pk')
        ).values('person').annotate(
            sum=Sum('amount')
        ).values('sum')[:1]
    ), Value(0)) - Coalesce(Subquery(
        Consumption.objects.filter(
            person=OuterRef('pk')
        ).values('person').annotate(
            sum=Sum('amount')
        ).values('sum')[:1]
    ), Value(0))
)

【讨论】:

以上是关于Django ORM注释Sum计算错误及其乘以条目数,它是有线的的主要内容,如果未能解决你的问题,请参考以下文章

通过 Django ORM 重命名嵌套注释字段

使用 Django ORM 注释查询以计算 2 个表值的总和

Django - 注释多个 Sum() 对象会给出错误的结果

075: Django数据库ORM聚合函数详解-Sum

Django注释返回重复条目

Django中ORM介绍和字段及其参数