Django查询 - 具有聚合功能的“case when”

时间:2010-09-26 18:04:38

标签: django django-models

我有以下django模型(映射到表'A'):

class A(models.Model):
    name = models.CharField(max_length=64, null=False)
    value = models.IntegerField()
    ...

我想在顶部执行以下简单查询:

select avg(case 
        when (value > 0 and value <= 50) then 0 
        when (value > 50 and value < 70) then 50 
        else 100 end) 
from A
where ...

我正在尝试避免原始SQL - 如何使用django实现(在上面的示例中我使用avg,但同样的问题也与max,min,sum等相关)?

我尝试使用extra和aggregate:

extra(select={'avg_field': case_when_query})

aggregate(Avg('avg_field')), 

但聚合函数仅适用于模型字段,因此此处不能使用额外字段。 如何用django完成?

感谢您的帮助

3 个答案:

答案 0 :(得分:4)

可以做什么仍然允许我们使用django queryset是这样的:

qs = A.objects.extra(select={"avg_field": 
                     "avg(case when...)"}).filter(...).values("avg_field")

使用结果:

qs[0]["avg_field"]

这将允许所需的功能。

答案 1 :(得分:3)

据我所知,(不幸的是)没有办法在不诉诸原始SQL的情况下完成您所描述的内容。

那就说 是一种计算平均值的方法,如果你愿意稍微对数据进行非规范化的话。例如,您可以添加一个名为average_field的新列,该列会自动设置为save()上的相应值。您可以覆盖save()或点按信号以自动执行此操作。例如,

class A(models.Model):
    name = models.CharField(max_length=64, null=False)
    value = models.IntegerField()
    average_field = models.IntegerField(default = 0)

    def _get_average_field(self):
        # Trying to match the case statement's syntax.
        # You can also do 0 < self.value <= 50
        if self.value > 0 and self.value <= 50:
            return 0
        elif self.value > 50 and self.value < 70:
            return 50
        else:
            return 100

    def save(self, *args, **kwargs):
        if self.value:
            self.average_field = self._get_average_field()
        super(A, self).save(*args, **kwargs)

一旦你这样做,你的查询变得非常容易。

A.objects.filter(...).aggregate(avg = Avg('average_field'))

答案 2 :(得分:3)

Django 1.8将支持开箱即用的CASE WHEN表达式,请参阅https://docs.djangoproject.com/en/dev/ref/models/conditional-expressions/