同时使用两个注释表达式时出现错误的结果

时间:2017-07-20 21:39:31

标签: python django python-3.x django-models orm

假设以下表格:

class Table1(models.Model): 
    Column1 = models.IntegerField()
class Table2(models.Model):  
    Column2 = models.IntegerField()
class Table3(models.Model):  
    Table1 = models.ForeignKey(Table1, null=False, on_delete=models.CASCADE)
    Table2 = models.ForeignKey(Table2, null=False, on_delete=models.CASCADE)
    Column3 = models.IntegerField()
class Table4(models.Model):  
    Table1 = models.ForeignKey(Table1, null=False, on_delete=models.CASCADE)
    Table3 = models.ForeignKey(Table3, null=False, on_delete=models.CASCADE)
    Column4 = models.IntegerField()

此注释表达式返回正确的答案:

print(Table1.objects.annotate(Exp1=Sum(
            Case(
                When(table3__Table2__Column2__in=[2, 3],
                     then=F('table3__Column3')),
                default=Value(0)
            ),
    )).values('Exp1'))

那是:

<QuerySet [{'Exp1': 96}]>

我需要定义另一个注释表达式,如下所示:

print(Table1.objects.annotate(Exp2=Sum(
            Case(
                When(table4__Table3__Table2__Column2=3,
                     then=F('table4__Column4')),
                default=Value(0)
            ),
      )).values('Exp2'))

结果再次正确:

<QuerySet [{'Exp2': 0}]>

最后,我想将这两个结合在一个命令中:

print(Table1.objects.annotate(Exp1=Sum(
            Case(
                When(table3__Table2__Column2__in=[2, 3],
                     then=F('table3__Column3')),
                default=Value(0)
            ),
    ), Exp2=Sum(
            Case(
                When(table4__Table3__Table2__Column2=3,
                     then=F('table4__Column4')),
                default=Value(0)
            ),
    )).values('Exp1', 'Exp2'))

但不幸的是结果不正确:

<QuerySet [{'Exp2': 0, 'Exp1': 480}]>

2 个答案:

答案 0 :(得分:1)

使用annotate多个聚合时需要注意:

  

将多个聚合与annotate()组合将产生错误   结果是因为使用了连接而不是子查询。   对于大多数聚合,没有办法避免这个问题......

与使用Count的聚合不同,Sum不会使用可帮助重复项目的distinct参数。如果要获得正确的结果,我想你应该保留不同的查询。

请参阅Combining multiple aggregations

答案 1 :(得分:0)

报告错误here但即使在Django 1.11中也没有解决。该问题与以反向关系连接两个表有关。 请注意,distinct参数适用于Count但不适用于Sum。所以我使用了一个技巧并编写了下面的ORM,效果非常好:

print(Table1.objects.annotate(
    temp_Exp1=Sum(
            Case(
                When(table3__Table2__Column2__in=[2, 3],
                     then=F('table3__Column3')),
                default=Value(0)
            ),), 
    temp_Exp2=Sum(
            Case(
                When(table4__Table3__Table2__Column2=3,
                     then=F('table4__Column4')),
                default=Value(0)
            ),),
    table3_count=Count('table3'),
    table3_count_distinct=Count('table3', distinct=True),
    table4_count=Count('table4'),
    table4_count_distinct=Count('table4', distinct=True),
).annotate(
    Exp1=F('temp_Exp1')*F('table3_count_distinct')/F('table3_count'),
    Exp2=F('temp_Exp2')*F('table4_count_distinct')/F('table4_count'),
).values('Exp1', 'Exp2'))