带有聚合的 Django 子查询

Posted

技术标签:

【中文标题】带有聚合的 Django 子查询【英文标题】:Django subquery with aggregate 【发布时间】:2019-04-30 16:45:12 【问题描述】:

我有两个模型,称为 UserTransaction 。在这里,我想获取状态为成功的交易金额总和的所有用户。

我尝试过使用子查询,但我不知道如何使用条件注释子查询的聚合

class User(models.Model):
  name = models.CharField(max_length=128)

class Transaction(models.Model):
  user = models.ForeignKey(User)
  status = models.CharField(choices=(("success", "Success"),("failed", "Failed")))
   amount = models.DecimalField(max_digits=10, decimal_places=2)

subquery = Transaction.objects.filter(status="success", user=OuterRef('pk')).aggregate(total_spent = Coalesce(Sum('amount'), 0))

query = User.objects.annotate(total_spent=Subquery(subquery:how to do here ?)).order_by(how to order here by total_spent)

【问题讨论】:

【参考方案1】:

django-sql-utils 包使这变得容易得多。

from django.db.models import Sum,
from sql_util.utils import SubqueryAggregate

User.objects.annotate(
    total_spend=SubqueryAggregate('transaction__amount',
                                  filter=Q(status='success'),
                                  aggregate=Sum)
)

如果你想长期做下去(没有 django-sql-utils),你需要知道关于子查询的这两件事:

    使用前无法评估

    只能返回单列单条记录

因此,您不能在子查询上调用aggregate,因为这会立即评估子查询。相反,您必须注释该值。您还必须按外部 ref 值进行分组,否则您只需单独注释每个 Transaction。

subquery = Transaction.objects.filter(
        status='success', user=OuterRef('pk')
    ).values(
        'user__pk'
    ).annotate(
        total_spend=Sum('amount')
    ).values(
        'total_spend'
    )

第一个 .values 导致正确的分组依据。第二个.values 导致选择您想要的一个值。

【讨论】:

请问,如果amount 也是一个注释字段呢? __amount 似乎不适用于这样的子查询字段。 @DingkunLiu,子查询是正确的。里面没有__amount。如果amountTransaction 对象的注释字段,则子查询不会改变【参考方案2】:

你可以这样做:

subquery = Transaction.objects.filter(
    status="success", user=OuterRef('pk')
).annotate(
    total_spent = Coalesce(Func('amount', function='Sum'), Decimal(0))
).values('total_spent')

query = User.objects.annotate(
    total_spent=Subquery(subquery)
).order_by('total_spent')

您可以在此答案中看到有关此方法的更多详细信息:https://***.com/a/69020732/10567223

【讨论】:

如果您遇到奇怪的“分组依据”错误,请将 .order_by 添加到子查询的末尾。 newbedev.com/django-conditional-subquery-aggregate【参考方案3】:

你可以点击这个查询:

from django.db.models import Avg, Count, Min, Sum

User.objects.filter(status="success").annotate(total_amount=Sum('transaction__amount'))

【讨论】:

【参考方案4】:

当模型上设置了订购时,建议的解决方案对我不起作用。

class InstallmentReservation(models.Model):
    class Meta:
        ordering = ['id']

我需要清除订单以使其再次工作。

    subquery.query.clear_ordering(True)

整个代码示例 - 查询集上的方法 - 希望对您有所帮助

def with_installment_reservations_amounts(self):
    """
    Sum of initial amount of active installment reservations annotated in _installment_reservations_initial_amount
    Sum of principal amount of active installment reservations annotated in _installment_reservations_amount
    `.values('customer')` in subquery is used to properly sum values. See https://***.com/questions/55925437/django-subquery-with-aggregate for more details.
    also this does not work when there is an ordering set on a model for some reason, so we need to clear it.
    """

    reservation_query = InstallmentReservation.objects.filter(customer_id=OuterRef('pk')).active().values('customer')
    reservation_query.query.clear_ordering(True)

    return self.annotate(
        _installment_reservations_amount=Coalesce(Subquery(reservation_query.annotate(sum=Sum('amount_principal')).values('sum')[:1]), Decimal(0),),
        _installment_reservations_initial_amount=Coalesce(Subquery(reservation_query.annotate(sum=Sum('initial_installment_amount')).values('sum')[:1]), Decimal(0),),
    )

【讨论】:

【参考方案5】:

要使用子查询,请使用:

query=User.objects.annotate(total_spent=Subquery(subquery.values("user")[:1])).order_by("total_spent")

【讨论】:

问题要求 SUM 但不是查询集中的第一个实例。

以上是关于带有聚合的 Django 子查询的主要内容,如果未能解决你的问题,请参考以下文章

如何编写带有子查询的 Django 查询作为 WHERE 子句的一部分?

在 Django 的 ORM 中使用带有 UPDATE 的子查询

无法在具有聚合值的 HAVING 子句中使用来自子查询表连接的单值列

子查询中的 Django 回合

没有聚合或子查询的查询

聚合查询+联合查询+子查询