【问题标题】:Django and Aggregate: Sum of distinct values?Django 和聚合:不同值的总和?
【发布时间】:2011-05-21 06:28:33
【问题描述】:

我正在尝试执行 django 聚合函数,但无法产生所需的结果。

我有什么:

income_posts.values_list('category__name','amount')
[(u'Donation', Decimal("2000.00")), (u'Paycheck', Decimal("1200.00")), (u'Donation', Decimal("1000.00"))]

想要的结果:

[(u'Donation', Decimal("3000.00")), (u'Paycheck', Decimal("1200.00))]

我需要对具有相同 category__name 的“金额”字段求和。

【问题讨论】:

    标签: django django-orm


    【解决方案1】:

    来自this answer for a related question

    from django.db.models import Sum
    income_posts.values('category__name').order_by('category__name').annotate(total=Sum('amount'))
    

    【讨论】:

    【解决方案2】:

    如果您使用的是 Postgres,则可以使用 django-pg-utils package 来计算不同值的总和。

    from pg_utils import DistinctSum
    income_posts.annotate(total=DistinctSum('amount')
    

    【讨论】:

    • django-pg-utils 的 Github 不再可用,最新更新是在 2017 年。它仍然有效,但请注意此模块。
    【解决方案3】:

    只是为了添加到 arjun27 的答案。由于该软件包似乎已被放弃,您可能只想复制过去所需的 3 行:

    from django.db.models import Sum
    class DistinctSum(Sum):
        function = "SUM"
        template = "%(function)s(DISTINCT %(expressions)s)"
    

    可以和上面一样使用:

    income_posts.annotate(total=DistinctSum('amount')
    

    【讨论】:

    • 很好,但相同数量来自不同来源的情况除外
    【解决方案4】:

    Django 3.0 在 Sum 和 Avg 上引入了“distinct=True”: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#sum

    【讨论】:

      【解决方案5】:

      对于那些使用 django 2.2 LTE 的人来说,这种行为可以通过复制 django 3.0 提交来实现,该提交实现了对 Sum 的不同:

      https://github.com/django/django/commit/5f24e7158e1d5a7e40fa0ae270639f6a171bb18e

      这样:

      from django.db.models Sum
      
      class SumDistinctHACK(Sum):
          allow_distinct = True
      

      现在你可以使用 django 3.0 语法了:

      queryset.annotate(
          sum_result=SumDistinctHACK(
              'relatedmodel__values_to_sum',
              distinct=True,
          )
      )
      

      如果升级到django >= 3.0,记得将SumDistinctHACK替换为Sum

      【讨论】:

        【解决方案6】:

        对于旧版本的 Django,请使用 Func

        queryset.annotate(
           sum_result=Sum(
                Func(F('amount'), function='DISTINCT')
           )
        )
        

        【讨论】:

        • 只要你有不同的值(数量),这就会起作用。一旦你有了相同的值,它就不再起作用了。
        【解决方案7】:

        你可以这样做:

        income_posts.values("category__name").distinct().annotate(total=Sum("amount"))
        

        【讨论】:

          【解决方案8】:

          我认为这个问题也与Combining multiple aggregations有关。

          Here is the ticket 这个错误。

          我们可以使用Subquery(Django Docs) 来解决这个问题:

          from django.db.models import Subquery, OuterRef, IntegerField, Sum, Value, Count
          
          MyModel.objects.annotate(
              count_model_a=Count('ModelA', distinct=True), 
              sum_model_b=Coalesce(
                  Subquery(
                      ModelB.objects.filter(
                          MyModel=OuterRef('pk')
                      ).values('MyModel_id').annotate(
                          my_sum=Sum('MyModel_Field')
                      ).values('my_sum')[:1],
                      output_field=IntegerField()
                  ),
                  Value(0)
              )
          ).values("count_model_a", "sum_model_b")
          

          我还使用了Coalesce(Django Docs) 函数来防止返回None

          以上代码将对 DB 运行一次查询。

          【讨论】:

          • 出色的答案。这是该问题最被低估的答案。我想知道为什么这没有得到足够的支持
          猜你喜欢
          • 2012-05-04
          • 2014-07-27
          • 2016-01-30
          • 1970-01-01
          • 2021-11-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多