【问题标题】:Django query - "case when" with aggregation functionDjango 查询 - 带有聚合函数的“case when”
【发布时间】:2011-04-17 11:32:25
【问题描述】:

我有以下 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(select={'avg_field': case_when_query})

aggregate(Avg('avg_field')), 

但聚合函数仅适用于模型字段,因此此处不能使用额外字段。 django如何做到这一点?

感谢您的帮助

【问题讨论】:

    标签: django django-models


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      仍然可以让我们使用 django 查询集的方法如下:

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

      使用结果:

      qs[0]["avg_field"]
      

      这将允许所需的功能。

      【讨论】:

        【解决方案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'))
        

        【讨论】:

        • 谢谢。不幸的是,我无法将这些值存储在数据库表中,因为“case when”值可能会根据我要执行的计算而有所不同。
        • @Li:明白。我明白为什么这在这种情况下不起作用。
        • 再次感谢您的帮助。最终使用额外的 + 值得到了我想要的结果。
        猜你喜欢
        • 1970-01-01
        • 2018-04-21
        • 2022-12-15
        • 2019-10-16
        • 1970-01-01
        • 2018-11-15
        • 2021-05-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多